注意:实际上,replaceReducer 中的 dispatch({ type: '@@redux/INIT' }),只有此时 currentState 是 undefined 时,才有作用,会把新的 initialState 赋值给 currentState。
function createStore(reducer, preloadedState) { let currentReducer = reducer let currentState = preloadedState; let currentListeners = []; function getState() { // 取 return currentState; } function dispatch(action) { // 存 currentState = currentReducer(currentState, action); currentListeners.forEach(fn => fn()); return action; } function subscribe(listener) { // 发布订阅 currentListeners.push(listener); } function replaceReducer(nextReducer) { // 重置reducer currentReducer = nextReducer; dispatch({ type: '@@redux/INIT' }); // 重置 } dispatch({ type: '@@redux/INIT' }); // 初始化 return { getState, dispatch, subscribe, replaceReducer } }createStore 的实现到这里已经完成,Redux 源码除此之外还做了大量的错误校验。
combineReducers
随着项目越来越大,把 reducer 放在一个文件里写会越来越臃肿,于是 Redux 提供了 combineReducers 方法。
先来看下如何使用
rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer}) // rootReducer 将返回如下的 state 对象 { potato: { // ... potatoes, 和一些其他由 potatoReducer 管理的 state 对象 ... }, tomato: { // ... tomatoes, 和一些其他由 tomatoReducer 管理的 state 对象,比如说 sauce 属性 ... } }combineReducers 参数是 reducers 对象,返回一个合成后的 reducer。
实现逻辑比较简单,循环把 reducers 里的每一个 reducer 都执行, 执行结果放在 nextState 里,如果数据改变了就返回 nextState,如果数据没有改变就返回传入的 state。
注意:如果数据没有改变,返回的是传入的 state,虽然此时和 nextState 数据是一样的,但是实际地址并不一样。为了区分,Redux 特意用了 hasChanged 变量来记录。
function combineReducers(reducers) { const reducerKeys = Object.keys(reducers); // key[] return function combination(state = {}, action) { let hasChanged = false; // state 是否改变 const nextState = {}; // 改变后的 state // 循环 reducers reducerKeys.forEach(key => { const reducer = reducers[key]; // 当前 reducer const previousStateForKey = state[key]; // 当前 state const nextStateForKey = reducer(previousStateForKey, action); // 如果 没有匹配到action.type,会在 reducer 中的 switch default 返回传入的 state,即 previousStateForKey nextState[key] = nextStateForKey; hasChanged = hasChanged || nextStateForKey !== previousStateForKey; }) return hasChanged ? nextState : state; } }bindActionCreators
bindActionCreators(actionCreators, dispatch) 把一个 value 为不同 action creator 的对象,转成拥有同名 key 的对象。
action 生成器名字叫做叫 action creator, 如下
function addTodo(text) { return { type: 'ADD_TODO', text, }; }修改数据需要这样写
dispatch(addTodo('Use Redux'))如果我们多个 action creator,写起来会比较繁琐,
dispatch(addTodo('Use Redux')) dispatch(plusTodo()) dispatch(setDataTodo({ id: 1 }))所以 Redux 提供了 bindActionCreators 函数,传入 action creators 和 dispatch, 返回绑定了 dispatch 的 action creators。
实现也很简单,遍历 actionCreators, 把每个元素用 dispatch 处理后生成新的函数,返回新函数的集合。
actionCreators 参数是 action creator 的集合对象,如 { addTodo, addTodo1 }。实现代码如下:
function bindActionCreators(actionCreators, dispatch) { const boundActionCreators = {}; Object.keys(actionCreators).forEach(key => { const actionCreator = actionCreators[key]; if (typeof actionCreator === 'function') { boundActionCreators[key] = (...args) => dispatch(actionCreator(...args)); } }) return boundActionCreators; }使用 bindActionCreators 写起来就会方便很多
const boundActionCreators = bindActionCreators({ addTodo, plusTodo, setDataTodo, }, dispatch); // 写入数据 boundActionCreators.addTodo('Use Redux') boundActionCreators.plusTodo() boundActionCreators.addTodo({ id: 1 })Redux 支持 actionCreators 是一个单个 action creator 的函数,所以提取公共方法。改造如下:
function bindActionCreator(actionCreator, dispatch) { return (...args) => dispatch(actionCreator(...args)); } function bindActionCreators(actionCreators, dispatch) { if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch) } const boundActionCreators = {}; Object.keys(actionCreators).forEach(key => { const actionCreator = actionCreators[key]; if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch); } }) return boundActionCreators; }compose
从右到左来组合多个函数。