51好读  ›  专栏  ›  Huanqiang

读 Redux 源码笔记

Huanqiang  · 掘金  ·  · 2019-08-02 10:10

正文

阅读 6

读 Redux 源码笔记

1. Redux 基础

Redux 三大原则

  1. 单一数据源;( redux 只有一个数据源,)
  2. State 只读; ( State 不能被直接修改,只能 dispatch 一个 action 的方式来返回一个新的状态);
  3. 只能通过纯函数( reducer )去修改 store ;( reducer 描述了如何将 action 里的信息和原 state 组合成一个新的状态)

如何使用

Redux 中文文档 Redux 文档已经详细的描述了 Redux 的基础使用、进阶和一些技巧, 务必详细阅读

简单来说,首先我们需要创建一个 store ,用于存放 State

const configStore => () {
	const store = createStore(reducers, initState, middlewares)
	reutrn store
}
复制代码

然后创建各个 reducer ,并使用 combineReducer 来合成它们。

const count = (state={}, acrtion) => {...}
const name = (state={}, acrtion) => {...}

export default combineReducer({
	count,
	name	
})
复制代码

然后创建需要的 action ,通常我们使用 action creater 来创建 action action 描述发生了什么,它承载了当前操作的操作类型和数据

const addCount = (count) => ({
	type: "ADD",	// 这个表示当前action的操作类型,在reducer中需要依据action type 来执行相应的操作;
	count					// action 的操作数据
})
复制代码

然后只要使用 dispatch(addCount(xxx)) ,就能修改 store 里的 count state 了。

但是通常情况下,我们会借助第三方库 react-redux 来帮忙封装,所以不需要直接手动在组件中操作 dispatch ,库 react-redux ,将这个操作移到了 mapDispatchToProps 中了。

详见 react-redux 文档

本文将总结阅读源码后的感触。

2. Reducer combineReducers

Redux 中,我们可以使用 reducer 来创建新的 state

第一次创建 state 是什么时候?

我们知道即使我们没有给 createStore 传递 initState Redux 也会生成一个初始 state 树,如下面的例子, redux 会返回 { count: 0, name: { first: 'huanqiang', last: 'wang' } } ,那么这是如何实现的呢?

const countReducer = (state = 0, action) => {
  switch (action.type) {
    case 'ADD':
      return state + 1
    case 'DIV':
      return state - 1
    default:
      return state
  }
}

const firstNameReducer = (state = 'wang', action) => {
  switch (action.type) {
    case 'FIRST_LONG_CHANGE':
      return 'huanqiang'
    case 'FIRST_SHORT_CHANGE':
      return 'hq'
    default:
      return state
  }
}

const lastNameReducer = (state = 'huanqiang', action) => {
  switch (action.type) {
    case 'LAST_LONG_CHANGE':
      return 'wang'
    case 'LAST_SHORT_CHANGE':
      return 'w'
    default:
      return state
  }
}

const nameReducer = combineReducer({
  first: firstNameReducer,
  last: lastNameReducer
})

const reducers = combineReducer({
  count: countReducer,
  name: nameReducer
})
复制代码

如果你有心的话,当你打印了所有的 action 的时候,你在应用启动后会在 chrome console 窗口看到 @@redux/INITxxxxxxx 字样的信息,这个信息就表示 Redux 进行了 State 的初始化, Redux 在你执行 createStore 的时候,在 return 之前进行了一次 dispatch ,而 action 正是 { type: ActionTypes.INIT }

reducers 是什么样子的?

其实这个问题和 combineReducer 会得到一个怎样结构的返回值是一样的。

我们把上面的例子中的 reducers 简化一下其实就是如下函数,接受最顶层的 state ,然后返回一棵 state 的树。同时 Redux 处理每一个子 reducer 的时候,都会从树中获取属于该 reducer state ,然后再作为参数传递进去,比如 countReducer Redux 就是先获取属于 count 的状态 state['count'] ,然后和 action 一起作为参数传入 countReducer ,并将其返回值作为 count value

function(state={}, action) {
	return {
		count: countReducer(state['count'], action),
		name: (state1={}, action) => {
			first: firstNameReducer(state1['first'], action),
  		last: lastNameReducer(state1['last'], action)
		}(state['name'], action)
	}
}
复制代码

这里我们放一个简化的 combineReducers 的源码:

这个是我自己实现的,和官网的略有出入。

export default reducers => {
  return (state = {}, action) => {
    // 初始化新的state
    const nextState = {}
    const reducerKeys = Object.keys(reducers)

    let hasChanged = false
    for (const key of reducerKeys) {
      // key 就是我们传入 combineReducers 函数的 object 的 key 值,也就是 state 的 key。比如 上文的 count。
      // reducer 就是我们传入 combineReducers 函数的 object 的 value。
      const reducer = reducers[key]
      // 获取之前的 state
      const prevState = state[key]
      // 执行 reducer 函数
      nextState[key] = reducer(prevState, action)

      if (!hasChanged) {
        hasChanged = prevState !== nextState[key]
      }
    }
    return hasChanged ? nextState : state
  }
}
复制代码

看吧, combineReducers 函数是如此简单,以至于都没什么好写的,至于 dispatch 函数,其实更加简单,去掉边界条件判断和监听操作之后,核心 change state 的代码就这么一行。

currentState = currentReducer(currentState, action)
复制代码

然后是 getState ,去掉边界条件判断也超级简单:

function getState() {
  return currentState
}
复制代码

3. applyMiddleware

首先,先上一个中间件的示例:

来自于 Redux 中文文档 - Middleware 一文 最下方的例子

/**
 * 记录所有被发起的 action 以及产生的新的 state。
 */
const logger = store => next => action => {
  console.group(action.type)
  console.info('dispatching', action)
  let result = next(action)
  console.log('next state'






请到「今天看啥」查看全文