# 多文件协作
# reducer的拆分和合并
这一小节我们来处理下reducer的问题。啥问题?
我们知道reducer是一个计划函数,接受老的state,按计划返回新的state。那我们项目中,有大量的state,每个state都需要计划函数,如果全部写在一起会是啥样子呢?
所有的计划写在一个reducer函数里面,会导致reducer函数及其庞大复杂。按经验来说,我们肯定会按组件纬度来拆分出很多个reducer函数,然后通过一个函数把他们合并起来。
我们来管理两个state,一个counter,一个info。
const state = {
counter: {
count: 0
},
info: {
name: '前端九部',
description: '我们都是前端爱好者!'
}
};
他们各自的reducer
// counterReducer 是一个子reducer
// 注意: counterReducer接受的state是state.counter
function counterReducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return {
count: state.count + 1
}
case 'DECREMENT':
return {
...state,
count: state.count - 1
}
default:
return state;
}
}
// InfoReducer 是一个子reducer
// 注意: InfoReducer接受的state是state.info
function InfoReducer(state, action) {
switch (action.type) {
case 'SET_NAME':
return {
...state,
name: action.name
}
case 'SET_DESCRIPTION':
return {
...state,
description: action.description
}
default:
return state;
}
}
那我们用combineReducers函数来把多个reducer函数合并成一个reducer函数。大概这样用
const reducer = combineReducers({
counter: counterReducer,
info: InfoReducer
});
我们尝试实现下combineReducers函数
function combineReducers(reducers) {
// reducerKeys = ['counter', 'info']
const reducerKeys = Object.keys(reducers);
// 返回合并后的新的reducer函数
return function combination(state = {}, action) {
// 生成的新的state
const nextState = {};
// 遍历执行所有的reducers,整合成为一个新的state
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i];
const reducer = reducers[key];
// 之前的 key 的 state
const previousStateForKey = state[key];
// 执行 分reducer,获得新的state
const nextStateForKey = reducer(previousStateForKey, action);
nextState[key] = nextStateForKey;
}
return nextState;
}
}
我们来尝试下combineReducers的威力吧
const reducer = combineReducers({
counter: counterReducer,
info: InfoReducer
});
const initState = {
counter: {
count: 0
},
info: {
name: '前端九部',
description: '我们都是前端爱好者!'
}
};
const store = createStore(reducer, initState);
store.subscribe(() => {
const state = store.getState();
console.log(state.counter.count, state.info.name, state.info.description);
});
// 自增
store.dispatch({
type: 'INCREMENT'
});
// 修改 name
store.dispatch({
type: 'SET_NAME',
name: '前端九部2号'
});
本小节完整源码见demo-3 (opens new window)
# state的拆分和合并
上一小节,我们把reducer按组件纬度拆分了,通过combineReducers合并了起来。但是还有个问题,state我们还是写在一起的,这样会造成state树很庞大,不直观,很难维护。我们需要拆分,一个state,一个reducer写一块。
这一小节比较简单,我就不卖关子了,用法大概是这样(注意注释)
// counter 自己的 state 和 reducer 写在一起
const initState = {
count: 0
};
function counterReducer(state, action) {
// 注意: 如果state没有初始值,那就给他初始值!!
if (!state) {
state = initState;
}
switch (action.type) {
case 'INCREMENT':
return {
count: state.count + 1
}
default:
return state;
}
}
我们修改下createStore函数,增加一行dispatch({ type: Symbol() })
const createStore = function (reducer, initState) {
const state = initState;
const listeners = [];
function subscribe(listener) {
listeners.push(listener);
}
function dispatch(action) {
state = reducer(state, action);
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener();
}
}
function getState() {
return state;
}
// 注意: 只修改了这里,用一个不匹配任何计划的type,来获取初始值
dispatch({ type: Symbol() });
return {
subscribe,
dispatch,
getState
}
}
我们思考下这行可以带来什么效果?
createStore的时候,用一个不匹配任何type的action,来触发
state = reducer(state, action)
因为action.type不匹配,每个子reducer都会进到default项,返回自己初始化的state,这样就获得了初始化的state树了。
你可以试试
// 这里没有传 initState 哦
const store = createStore(reducer);
// 这里看看初始化的 state 是什么
console.dir(store.getState());
本小节完整源码见demo-4 (opens new window)
到这里为止,我们已经实现了一个七七八八的redux啦!
← 状态管理器 中间件middleware →