react学习总结系列-redux&react-redux

本篇将介绍redux跟react-redux。这里打算是介绍这两个插件在react项目中的运用。redux是可以在其他库中使用的。

前面使用react开发项目,是可以实现大部分需求的。但是react毕竟是一个实现视图层的框架,它在数据层的实现还是存在一些不足的。比如当项目较大,需要使用大量的数据,父子组件之间的数据通信特别频繁的时候,性能就会直线下滑;当数据发生改变的时候,就无从得知数据是从哪里发生改变的。

redux作为一个应用数据流框架,它的最大的特点就是应用状态的管理,它用一个单独的常量状态树store保存整个应用的状态。这个状态树不能直接被改变。

我们分析一下。如果没有redux,react组件的所有状态state将各自存储于组件中,一旦遇到不同父组件的组件之间的通信问题,需要把数据先传到能使两个组件通信的上层组件,然后在下发到另一个组件。而如果使用redux,则数据都存储在常量状态树store中,所有组件获取状态数据都从store中直接获取。而组件需要修改状态数据时,只需要直接修改store中的数据,其他组件就会获知store的变化并自动重新获取状态。这样可以减少组件之间大规模的通讯消耗。

redux工作流程

我们了解了react的组件components跟redux的常量状态树store,那他们是怎么串连起来,是怎么样的工作流程呢?

这里需要考虑三个场景,一个是components需要修改store中的数据,一个是store中实现数据的更新,还有一个就是store的数据更新之后新状态同步到components中。

最主要是前两个场景。这里需要引入redux中两个概念。

actionCreator

因为常量状态树store的状态数据不允许直接修改,所以当components需要修改store中的数据时,需要通过actionCreator中的action去让store做数据的更新。

actionCreator是一个action的集合,这个集合中包含了各种用于申请修改的action。这个action需要包括修改数据的类型跟需要修改的数据。在components发出store.dispatch(action)之后提交给store,完成修改申请。

reducer

store得到action时,它本身没有什么方法可以修改数据,所以需要用到reducer来实现状态的更新。store会把当前的state跟前面接收到的action传给reducerreducer会根据传入的action类型进行状态更新,然后再返回新的state

组件的状态更新

关于最后store的数据更新之后新状态同步到components中,阮一峰的《Redux 入门教程(一):基本用法》中有提到,当storestate发生变化后,store会调用监听函数,监听函数就可以更新组件中的state,组件就可以重新渲染页面。

react-redux实战

安装react-redux

在react项目中,引入react-redux还需要引入redux:

1
2
npm install redux --save
npm install react-redux --save

这样在react项目中才可以使用react-redux。

创建store

先在项目中创建一个store的文件夹,然后在创建一个index.js的js文件。在这个js文件中写下以下代码:

1
2
3
4
5
import { createStore } from 'redux';

const store = createStore(reducer);

export default store;

首先,先从redux中引入createStore,然后通过createStore来创建一个store,最后导出。

创建reducer

上面我们已经完成store的创建,然后需要创建reducer来给store处理数据。在store目录下创建一个reducer.js的js文件。redux中,reducer其实是一个函数,所需要引入的参数为当前的state(preState)action,而返回的是一个修改后的state(newState)。一般初始化的时候,没有当前的state(preState),所以会先声明一个defaultState

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const defaultState = {
/*
*这里初始化数据
*/
};

export default (state = defaultState, action) => {
switch (action.type) {
/*
* 这里判断action.type,然后修改获得newState
*/
}
return newState;
}

上面的代码中,先声明一个defaultState作为state的初始化状态,然后导出一个函数,这个函数中通过判断action.type来修改获得newState,然后返回出去。

创建之后,需要把reducer传入到store中,因此上面的index.js需要稍加修改:

1
2
3
4
5
6
import { createStore } from 'redux';
import reducer from './reducer';

const store = createStore(reducer);

export default store;

创建actionCreator

actionCreator还是要强调,它是一个方法的是一个action的集合,这个集合中包含了各种用于申请修改的action。当然,把所有action都只在需要的时候再编写也可以,但是把所有action集中起来可以提高代码的可读性,可维护性。每一个action方法最终都是返回一个对象,这个对象包含有修改类型type跟需要修改的值。在reducer.js中,大家可以看到传入的action.type是会在switch语句中作为判断值,然后修改值则会在判断后做对应的修改。

在store目录下创建一个actionCreator.js的js文件,然后写入所有需要导出的action方法:

1
2
3
4
5
6
7
8
9
export const action1 = (value1) => ({
type: actionType1,
value1: value1
})

export const action2 = (value2) => ({
type: actionType2,
value2: value2
})

在reducer中,action.type作为判断时,会再写一次。为了保证不出抄写错误,方便后期排查bug,一般还可以把actionType集中起来:

1
2
export const actionType1 = 'action_type_1';
export const actionType2 = 'action_type_2';

那么,在actionCreator中就可以改写成:

1
2
3
4
5
6
7
8
9
10
11
import * as constants from './actionType';

export const action1 = (value1) => ({
type: constants.actionType1,
value1: value1
})

export const action2 = (value2) => ({
type: constants.actionType2,
value2: value2
})

组件中的使用

组件中使用一般分引入store,调用action申请及store修改订阅。

组件中会现在构造函数中引入store:

1
2
3
4
constructor (props) {
super(props);
this.state = store.getState(); // 引入store,把store中的状态赋给state
}

当状态需要修改时,通过store.dispatch()调用action:

1
store.dispatch(actionCreators.action1(value1));

action方法返回对象将交给store,但是store不会处理,而是通过dispatch方法交给reducer处理。reducer接收到action后,会根据修改类型type对数据进行更新。在store的状态进行更新之后组件就需要更新。但是组件的状态更新还需要做一步操作,就是对store变化的订阅。这个订阅也是在constructor()完成:

1
2
3
4
5
constructor(props) {
super(props);
this.state = store.getState(); // 引入store,把store中的状态赋给state
store.subscribe(this.handleStoreChange); // 订阅store的状态修改,如果检测到修改,则调用this.handleStoreChange监听函数
}

以上就是对redux跟react-redux使用的总结。