React Redux管理状态

state是react特有的一个存储数据的地方,通过各种操作可以改变state的值,然后更新到页面显示上。
但是,当工程变得非常庞大且复杂的时候,各种各样的state和更新state的方法就会使程序变得很乱,管理前端这些数据,需要进行“有条理的对数据进行操作”。而进行这个操作的,就是Redux

Redux提供一些api来管理数据,Redux很霸道地告诉我们:数据只能存在我这,并且只能通过我的方式来修改

Redux包括三部分:storeactionreducer

store:一个规范的state,Redux将整个应用的state储存在唯一的store中
action:一个有属性的对象,使用dispatch(action)来触发,并且这是改变state的唯一方式
reducer:具体通过action更新state的函数,基本结构是reducer(state, action) => newstate

添加redux依赖

1
yarn add redux

创建store

在app.js中,引入redux包中的createStore()方法

1
import { createStore } from 'redux';

然后,通过createStore(reducers[, initialState])的方式来创建store,这个方法根据reducer生成store,并且只能通过此reducer来改变store中的状态,第二个参数是可选的默认初始值。

注意,一个应用中我们只有一个store,存储了全部数据状态,但是会有很多reducer,通过这些reducer合起来创建store需要用到combineReducers方法。

但是现在只做一个简单操作,所以用一个reducer做例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//初始状态
const initialState = {
data: [{
"key": "1",
"city": "杭州",
"province": "浙江"
},{
"key": "2",
"city": "广州",
"province": "广东"
}]
}

//创建reducer方法,先原封不动返回state
const myReducer = (state=initialState, action) => {
return state;
}
//创建store存储区,它只能通过reducer作为参数来构造
const store = createStore(myReducer);

这样store就创好了,可以把store打印到控制台看一下。getState()是store的一个最常见api,用了获取state的值。

1
console.log("initial state: ", store.getState());

Very well,接下来写一个更新state的action。

创建action

Action 是一个带属性的对象,其属性用type来定义,type是必填项,其他的还可以有附带数据,一般写在用payload里。

1
2
3
4
5
6
7
8
9
//创建描述性对象action
const myAction = {
type: 'ADD_DATA',
payload: {
"key": "3",
"city": "深圳",
"province": "广东"
}
};

上面已经说过,修改state的唯一方法就是dispatch(action),那么现在我们就把这个action触发一下。

1
store.dispatch(myAction);

如果是点击一个按钮触发的话,把dispatch放在onClick绑定的函数里就行。

1
2
3
4
5
6
7
8
9
10
11
12
13
//点击按钮触发
const changeData = () => {
store.dispatch(myAction);
}

class App extends Component {
render() {
return (
<Button type="primary" onClick={changeData}>修改数据</Button>
……
);
}
}

创建reducer

Store触发了一个action,相当于store宣称:“我要改变自己的state”,此时,需要reducer来执行这个过程。
Reducer的输入参数是当前的state和收到的action,返回一个新的state。注意在reducer中,不能随意更改参数或者加入随机性的各种操作,也就是reducer输入相同的话,输出也必定相同。

修改刚刚的reducer如下:

1
2
3
4
5
6
7
8
9
10
11
12
//创建reducer方法
const myReducer = (state=initialState, action) => {
switch (action.type) {
case 'ADD_DATA':
return {
...state,
data:state.data.concat(action.payload)
}
default:
return state;
}
}

…state是ES6中的三点运算符,作用是把数组打开进行操作。
可以看到,当action是ADD_DATA的时候,我们把payload里面的内容加在原有的data之后,也就是新加一行数据。

这时,在控制台输出store的值,可以看到已经有所改变。

通过监听更新视图

然而,现在视图上却没有刷新,只是store中的值发生改变。这是因为store跟view层并没有连起来,如果不手动重新render,页面是不会变化的,为此我们需要一个监听函数,监听store中值的变化,当发生变化时重新渲染view。

这个监听由store.subscribe实现。(如果使用了react-redux的话,它里面的connect让我们不需要自己手动去subscribe全局state的变化,它会在内部自动监听并更新。)

1
2
3
4
5
6
7
8
9
10
11
class App extends Component {
listener() {
let newState = store.getState();
this.setState(newState);
}

//保持监听
componentDidMount () {
store.subscribe(this.listener.bind(this));
}
}

Redux流程总结

这样,整个Redux流程实现完毕:

  • 创建store存储数据,并把初始值绑定到view层
  • 点击按钮,触发store.dispatch(myAction)
  • store收到action后,调用myReducer = (state=initialState, action)改变store中的state
  • store更新后,通过store.subscribe监听函数更新view层

完整代码:

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import React from 'react';
import { Button, Icon, Table, Row, Col } from 'antd';
import { createStore } from 'redux';

//初始状态
const initialState = {
data: [{
"key": "1",
"city": "杭州",
"province": "浙江"
},{
"key": "2",
"city": "广州",
"province": "广东"
}]
}

// //创建reducer方法,先原封不动返回state
// const myReducer = (state=initialState, action) => {
// return state;
// }
//创建reducer方法
const myReducer = (state=initialState, action) => {
switch (action.type) {
case 'ADD_DATA':
return {
...state,
data:state.data.concat(action.payload)
}
default:
return state;
}
}
//创建store存储区,它只能通过reducer作为参数来构造
const store = createStore(myReducer);

//创建描述性对象action
const myAction = {
type: 'ADD_DATA',
payload: {
"key": "3",
"city": "深圳",
"province": "广东"
}
};

class ReduxTest extends React.Component {
constructor(props) {
super(props);
this.state = {
columns: [{
title: '城市',
dataIndex: 'city',
}, {
title: '省份',
dataIndex: 'province',
}],
}
}

listener() {
let newState = store.getState();
this.setState(newState);
}

//保持监听
componentDidMount () {
store.subscribe(this.listener.bind(this));
}

readData() {
console.log("initial state: ", store.getState());;
}

addData() {
store.dispatch(myAction);
}

render() {
var data = store.getState().data;
var columns = this.state.columns;
return (
<div className="App">
<Row type="flex" justify="center">
<Button onClick={this.readData}>读取数据</Button>
<Button type="primary" onClick={this.addData.bind(this)}>添加数据</Button>
</Row>
<div>
<Table columns={columns} dataSource={data}/>
</div>
</div>
);
}
}

export default ReduxTest;

重要补充说明

在上面的例子里,我们只用了redux,事实上在React中,我们一般常用react-redux来进行操作。react-redux提供的结构可以让我们不需要自己手动去写dispatchsubscribe这些函数,它在内部通过自身的结构帮我们完成了这些操作。

Dva已经封装好了react-routerreact-reduxredux-saga等中间件,并给出了清晰地工程目录结构。因而在了解react基础知识后,用Dva来构建工程更加快捷简便。

Powered by AppBlog.CN     浙ICP备14037229号

Copyright © 2012 - 2020 APP开发技术博客 All Rights Reserved.

访客数 : | 访问量 :