redux实践

以下分享来自团队童鞋huangmin

redux是在看react时用于管理状态(state),之前一直发音错了(美[riː’dʌks]) 。

关于redux + react的例子有很多,很多都是结合react来讲,react-redux中的connect, Provider方便了与redux之间的联系。redux深入理解看这

本篇文章中主要提到单纯的去使用和理解redux

为什么用redux

在维护多个react组件时,当涉及到多重传递或者为了组件之间的通讯时,将所有的状态提到顶层再一层一层传递下去或者通过callBack来修改顶层的state, 这样做增加了维护成本,代码可阅读性。而redux是则可以解决react中组件与组件之间数据传递和通讯问题。

需要额外准备的内容:

es6: 箭头函数, 属性的简洁表示法

简介

redux 是 facebook 提出的 flux 架构的一种优秀实现, 不局限于为 react 提供数据状态处理, 可以配合其他框架处理数据。

Redux适用多数据,多交互的场景,设计思想就是用一个大的对象包裹所有的状态,每个状态对应不同的View。

下面是两张分别是fluxredux + react的原理图,引用来自阮老师的blog:

flux

redux + react

两者之前的共同点都是数据是单向流动的,都是action动作dispatch派发到store,store则负责维护应用的所有状态,当产生变化时,则会通知view渲染。

redux中有几个常见的名词

action:
action是view发出来的通知,一种动作;
action是修改store数据的唯一来源;
action对象里面type属性是必须的
dispatch:
store.dispatchview发出action的唯一方法,dispatch接受一个action作为参数,并将其发送给state
reducer:
reducerstate接收到action的计算过程,通过reducer计算后返回一个新的state
reducer是一个纯函数,相同的输入相同的输出
store:
actionreducer联系在一起,并保存state的数据,使用createStore函数创建。

store中有几个常用的方法

getState() : 获取store中的state
dispatch(action): 更新state
subscribe(listener): 注册和注销监听(返回函数注销监听)

redux中的单向的操作

action(动作) -> reducer(更新state) -> state(返回新的state) ->

action(动作) -> reducer(更新state) -> state(返回新的state) ->

为了确保每次同一个action所改变的内容都一样,所以reducer函数里面的内容应该是一个纯函数(详细参考FP,这个函数只处理state并返回一个新的state)

action

现在我们手写一个action和一个action创建函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const ADD = 'ADD';
//action
{
type: ADD,
text: 'Build my first Redux app'
}
// action 创建函数
let add = (text) => {
return {
type: ADD,
text
};
}

看起来很简单是吧,actionaction创建函数的区别在于有一个可复用的返回action的方法。

reducer

reducer是一个单纯的函数,只要传入参数相同,返回计算得到的下一个 state 就一定相同。

没有不同的返回值、不改变变量,仅仅只是计算, 如:

1
let add3 = (num) => num + 3;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const ADD = 'ADD';
const SUB = 'SUB';
const initState = {
"add": 0,
"sub": 0
};
let reducerSimple = (state = initState, action) => {
switch (action.type) {
case ADD:
return Object.assign({}, state, {add: 1});
case SUB:
return Object.assign({}, state, {sub: 1});
default:
return Object.assign({}, state, initState);
}
};

reducerSimple函数所执行的内容是获取action中的type参数并没有修改state的同时返回一个对应的,新的state。

reducer的拆分

Redux提供了combineReducers方法能够经行reducer的拆分,当reducer需要维护的变量越多时,一个reducer方法会变得难以阅读,这时候则需要拆分reducer

combineReducers这个方法做的事情则是产生一个整体的reducer函数,state通过state里面的type去执行子类reducer里面的计算

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
// 假设有a,b,c三个子reducer
const reducer = combineReducers({
a: a,
b: b,
c: c
})
// 等同于
const reducer = combineReducers({
a
b
c
});
// 等同于
function reducer(state = {}, action) {
return {
a: a(state.a, action),
b: b(state.b, action),
c: c(state.c, action)
}
}
//如果需要更改key的值
const reducer = combineReducers({
myA: a,
myB: b,
myC: c
});
// 等同于
function reducer(state = {}, action) {
return {
myA: a(state.myA, action),
myB: b(state.myB, action),
myC: c(state.myC, action)
}
}

store

store就是用来处理action和reducer, 并注册事件监听和使用dispatch来更新state

1
2
3
4
5
6
let store = createStore(reducerSimple);
store.subscribe(() => console.log(store.getState()));
store.dispatch(add('I am add'));
store.dispatch(sub('I am sub'));

完整代码如下:

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
import { createStore } from 'redux';
// public var
const ADD = 'ADD';
const SUB = 'SUB'
// action
let add = (text) => {
return {
type: ADD,
text
};
};
let sub = (text) => {
return {
type: SUB,
text
};
};
// reducer
const initState = {
"add": 0,
"sub": 0
};
let reducerSimple = (state = initState, action) => {
switch (action.type) {
case ADD:
return Object.assign({}, state, {add: 1, sub: 0});
case SUB:
return Object.assign({}, state, {add: 0, sub: 1});
default:
return Object.assign({}, state, initState);
}
};
// store
let store = createStore(reducerSimple);
store.subscribe(() => console.log(store.getState()));
store.dispatch(add('I am add')); //{ add: 1, sub: 0 }
store.dispatch(sub('I am sub')); //{ add: 0, sub: 1 }

其他

继续值得优化的地方:

  • 将action, reducer, store, public var, … 分别放在不同的文件夹(actions,reducers,store,constnts),通过es6中module将不同的模块拆分
  • 使用Middleware封装log记录

还有更高级的功能去探索:

  • 异步action
  • 异步数据流

我个人觉得不是所有的项目都需要用到redux。开发过程某一项工具类或者框架的使用应该慎重考虑,在需要用到的地方使用才能发挥出最大的威力。