react笔记(3)react-redux数据管理

前期考虑不周的坑,后期总要来填的。— ginny Guo
在这个项目的时候技术选型的时候,因为“时间周期较短”和“产品交流不充分”,没能完全get到prd中的一些细节问题,当时感觉传统的状态管理已经完全hold住这个项目了,结果在后期出现了一个state对应多个view改变,多个action触发一个state改变等问题。多个组件保留状态需要多个copy,简直是灾难啊啊!!!更加难过的是,后期出现了渲染太慢的问题,严重影响用户体验,所以不得不重构引入Redux状态管理了。
本篇记录了react-redux的使用,以及项目前后设计的对比,为以后技术选型做一个铺垫。
本篇真的写了好几天啊,好难讲清楚Orz,以后还要填坑几次才行。

Redux

ReduxJavaScript 状态容器,提供可预测化的状态管理。
其数据流大致如下:
redux

Store

Redux 中只有一个单一的 Store ,存储了所有共享状态(以一个对象树的形式储存)。
合并后的reducer(之后讨论)作为参数传入store。

根目录下入口文件index.js中添加store:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import {createStore} from 'redux';
import App from './App'
import myreducer from './reducers';

const store = createStore(myreducers);

ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);

Action

Redux中把动作和状态独立,通过动作来改变状态。Action 是改变 Store 数据的唯一来源,包含 View 中数据变化、用户操作、服务器响应等等。

Redux中通过Action创建函数的结果(返回值是一个action对象),传给 dispatch 方法即可发起一次dispatch过程。
Store里能直接通过 store.dispatch() 调用dispatch方法。

Action本质上是一个对象,type是一个字符串常量,表示要执行的动作。
Action只有指定动作,不包含更新状态的方法,方法在下面的reducer中会提到。

触发AReducer的actions,存放于actions/index.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// some actions
export const AddSth = (data) => ({
type: 'sthAdd',
data
})

export const DelSth = (data) => ({
type: 'sthDel',
data
})

export const UpdateSth = (data) => ({
type: 'sthUpdate',
data
})

export const ClearList = (data) => ({
type: 'listClear',
data
});

Reducer

Reducer 是一个用于处理事件的纯函数,决定每个action如何改变应用的state。
在本项目中为了方便区分各个业务逻辑(互相独立),为每个业务逻辑编写一个reducer,存放于reducers文件夹中间,reduce文件夹中的index.js合并所有reduce,作为一个根级的reducer。

1
2
3
4
5
6
7
8
9
10
11
import {combineReducers} from 'redux';
import AReducer from 'reducers';
import BReducer from 'reducers';
import CReducer from 'reducers';

const myreducer = combineReducers({
AReducer,
BReducer,
CReducer,
});
export default myreducers;

单个reduce是形式为 (state, action) => state 的纯函数,state的形式可以是基本类型、数组、对象等等。在本项目中状态是存储的数据,数据用数组list来表示。

举例AReducer,代码如下:

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
47
48
// reducer 
import _ from 'lodash';

export function AReducer(list:[], action) {
const {data = {}} = action;
switch (action.type) {
case 'sthAdd': {
const arrIndex = _.findIndex(
list,
(item) => parseInt(item.id, 10) === parseInt(data.id, 10)
);
if (arrIndex > -1) {
list.splice(arrIndex, 1);
} else {
list.push(data);
}
return list;
}

case 'sthDel': {
const arrIndex = _.findIndex(
list,
(item) => parseInt(item.id, 10) === parseInt(data.id, 10)
);
if (arrIndex > -1) {
list.splice(arrIndex, 1);
}
return list;
}

case 'sthUpdate': {
list.data.map(item => {
if (parseInt(item.id, 10) === parseInt(data.id, 10)) {
item.xxx = data.xxx;
}
return item;
});
return list;
}

case 'listClear': {
return [];
}

default:
return list;
}
}

react-redux容器组件

react-redux 使用容器组件来把展示组件连接到Redux ,容器组件向Redux派发actions,同时监听state改变。
对于容器组件和展示组件的划分附录中有一篇官网推荐的阅读。目前来说,我在本项目中把需要处理共享数据的页面作为了容器组件,后期可能还要修改下。

可以使用 mapStateToProps 来订阅 Store,其原理相当于在Store上安装了一个监听器,当Store中state改变了,子组件重新渲染。
可以定义 mapDispatchToProps 方法接收dispatch()方法并返回期望注入到展示组件的props中的回调方法。
在本项目中把action作为props整合数据,相当于包了一层dispatch的执行。

假设AList为容器组件,AList中代码如下:

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
import React from 'react';
import {connect} from 'react-redux';

import AItem from '../../components/AItem';
import {AddSth} from './actions';

class AList extends React.Component {
this.state = {
aList: []
};

getBetList = () => {
const {list} = this.state;
return list || [];
}

listAddHandle = (obj) => {
const {AddSth} = this.props;
AddSth(obj);
}

render() {
const aList = this.getBetList();
return (<div className={'components-alist'}>
{aList.map((item) =>
<div key={item.id}>
<AItem data={item} onAdd = {this.listAddHandle.bind(this)}/>
</div>)}
</div>);
}
}

export const mapStateToProps = state => ({
aList: state.list
});

export default connect(mapItemToProps, {AddSth})(AList);

附录
Redux
Presentational and Container Components