Redux 多个 Reducer 分拆

Redux 中,对 state 进行计算重新返回的方法通常叫做 reducer,如果 state 比较简单,只在一个 reducer 中也可以清晰的看明白数据处理过程,但 redux 通常用来处理复杂的应用程序,state 结构通常比较庞大,结构比较复杂,如果都写到一个方法里面,非常不便于维护,此时就需要对 reducer 进行拆分成多个子 reducer

combineReducers

redux 提供的 combineReducers 用于将多个 reducer 合并成一个 reducer,该方法接收一个 Object 对象。

1
2
3
4
Object {
prop1: reducerProp1,
prop2: reducerProp2,
}

如上一个 Object 表示合并之后得到的 reducer 将处理两个属性 prop1prop2,处理函数分别是 reducerProp1reducerProp2

state

1
2
3
4
5
6
7
8
9
10
{
user: {
name: "Michael Cai",
age: 0
},
work: {
name: "IT",
years: 0
}
}

假设有如上一个 state 结构,user 表示用户,work 表示工作,接下来需要做的就是把 userwork 拆分出来放到不同的 reducer 中去处理更新 state

reducer

分别定义处理 userworkreducer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import {combineReducers} from 'redux';
function userName(state = '', action) {
if (action.type == 'USER_SET_NAME') {
return action.name;
}
return state;
}
function userAge(state = 18, action) {
if (action.type == 'USER_SET_AGE') {
return action.age;
}
return state;
}
const user = combineReducers({
name: userName,
age: userAge
});

上面的代码定义了 user 的两个属性的更新处理函数,这两个属性的更新,也把它们给拆分开了,并没有放到一个整体的更新 user 对象的方法里面去更新。

下面是更新 work 的流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function workName(state = 'gwy', action) {
if (action.type == 'WORK_SET_NAME') {
return action.name;
}
return state;
}
function workYears(state = 0, action) {
if (action.type == 'WORK_SET_YEARS') {
return action.years;
}
return state;
}
const work = combineReducers({
name: workName,
years: workYears
});

定义好子 reducer 后,再次通过 combineReducers 方法合并上面定义的两个 reducer :

1
2
3
4
const reducer = combineReducers({
user,
work
});

渲染页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import {createStore} from 'redux';
import React from 'react';
import ReactDOM from 'react-dom';
import {reducer} from './reducer.js';
const store = createStore(reducer, default_state);
const App = React.createClass({
render: function () {
return <div>
<div>
<button onClick={()=> {
store.dispatch({'type': 'USER_SET_NAME', name: 'James'})
}}>SetUserName</button>
<span>{store.getState().user.name}</span>
</div>
<div>
<button onClick={()=> {
store.dispatch({'type': 'USER_SET_AGE', age: 32})
}}>SetUserAge</button>
<span>{store.getState().user.age}</span>
</div>
<div>
<button onClick={()=> {
store.dispatch({'type': 'WORK_SET_NAME', name: 'Android'})
}}>SetWorkName</button>
<span>{store.getState().work.name}</span>
</div>
<div>
<button onClick={()=> {
store.dispatch({'type': 'WORK_SET_YEARS', years: 4})
}}>SetWorkAge</button>
<span>{store.getState().work.years}</span>
</div>
</div>
}
});
const render = ()=> {
ReactDOM.render(<App />, document.getElementById('root'));
};
render();
store.subscribe(render);

应用程序可以分别设置 work.namework.yearsuser.nameuser.age

createStore() 方法还可以直接传入一个默认的 state;

1
2
3
4
5
6
7
8
9
10
const default_state = {
user: {
name: 'Michael Cai',
age: 26
},
work: {
name: 'gwy',
years: 0
}
};

如果不传入默认的 state,则在 reducer 函数定义时,需要定义对应的默认值,就像上面定义的 workName()userName()state 参数都赋予了默认值;

More

除了使用 redux 提供的 combineReducers 之外,也可以自己实现,实际上 combineReducers 处理之后的 reducer 就像是下面这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const userReducer = (state = {name: '', age: 0}, action) => {
return {
name: userName(state.name, action),
age: userAge(state.age, action)
}
};
const workReducer = (state = {name: '', age: 0}, action) => {
return {
name: workName(state.name, action),
age: workYears(state.age, action)
}
};
const reducer = (state = {}, action) => ({
user: userReducer(state.user, action),
work: workReducer(state.work, action)
});