React 中使用 React-Redux 入门

React 是一个前端框架,而 Redux 是一种数据处理解决方案,两者之间没有任何直接联系,可以使用 Reduxstore.subscribe 函数订阅监听数据改变,然后手动刷新整个应用,但是这样刷新的效率低下,并不能最大化 React 的优点,而 React-Redux 则是专门用于将 ReactRedux 结合起来,让我们可以使用 state 函数的形式来描述界面,优化应用程序重新渲染。

开始之前需要了解两个概念:容器组件展示组件
ReduxReact 绑定库是基于 容器组件展示组件 分离 的开发思想。
两者的异同:

展示组件 容器组件
作用 描述如何展现(骨架、样式) 描述如何运行(数据获取、状态更新)
直接使用 Redux
数据来源 props 监听 Redux state
数据修改 从 props 调用回调函数 向 Redux 派发 actions
调用方式 手动 通常由 React Redux 生成

大部分的组件都应该是 展示型 的,一般需要少数的几个容器组件把它们和 Redux store 连接起来。

技术上讲可以直接使用 store.subscribe() 来编写容器组件。但不建议这么做因为就无法使用 React Redux 带来的性能优化。因此不要手写容器组件,都是使用 React-Reduxconnect() 方法来生成。

下面来改写上一节中的计算器,使用 React-Redux 来更新计算结果。

state 的结构不变,仍然是一个 int 值表示计算结果;
action 不变,共两个 action,加和减;
reducer 不变,仍然根据 action 的 type 和 num 做相应的加减运算;

step1: 定义展示组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React from 'react';
const App = React.createClass({
add: function () {
this.props.add(3);
// store.dispatch(increment(3));
},
dec: function () {
this.props.dec(2);
// store.dispatch(decrement(2));
},
render: function () {
return <div>
<button onClick={this.add}>ADD</button>
<span style={{'paddingLeft': '8px', 'paddingRight': '8px'}}>{this.props.state}</span>
<button onClick={this.dec}>DEC</button>
</div>
}
});
export default App;

和之前的组件定义不同的是,adddec 方法,不再直接使用 store 对象分发 action,而是使用 props 对象的 adddec 方法;另外一个不同点是 span 中显示的计算结果,也是使用 props 对象的 state 属性;propsadddec 方法和 state 属性在后面定义容器组件时会进行定义;

step2: 定义容器组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import App from './App.js';
import {increment, decrement} from './action.js';
import {connect} from 'react-redux';
const mapStateToProps = (state, ownProps) => ({
state: state
});
const mapDispatchToProps = (dispatch, ownProps) =>({
add: (num = 1)=> {
dispatch(increment(num));
},
dec: (num = 1) => {
dispatch(decrement(num));
}
});
const ContainerApp = connect(mapStateToProps, mapDispatchToProps)(App);
module.exports = ContainerApp;

使用 react-redux 提供的 connect 方法定义容器组件,需要提供3个参数

  • mapStateToProps : 将 redux 中的 state 转换给展示组件的 props 对象,该对象是一个 function,两个参数 (state, ownProps) 分别表示 redux 中的 state 和 展示组件的 props 对象,该方法需要返回一个 object 对象,对象的所有属性都会被添加到展示组件的 props 对象;
  • mapDispatchToProps : 将 redux 中分发 action 的行为转换给展示组件的 props 对象,该对象是一个 function,两个参数 (dispatch, ownProps) 分别表示 reduxstore.dispatch(action) 函数 和 展示组件的 props 对象,该方法需要返回一个 object 对象,对象的所有属性都会被添加到展示组件的 props 对象;
  • connectmapStateToPropsmapDispatchToProps 连接到一起,然后将展示组件传给 connect 返回的函数,这个函数返回的组件就是一个容器组件;

mapStateToProps

上面的例子中由于 state 是计算结果值 int,所以直接将 redux 中的 state 赋值给 展示组件的 state 属性;如果是其它 state 结构,则根据需要传入相应的 state 值,比如:

1
2
3
4
const mapStateToProps = (state, ownProps) => ({
userName: state.userName,
userAge: state.userAge
});

该代码则将 reduxstate 对象的 userNameuserAge 分离开转换给了展示组件。在展示组件中则使用 this.props.userNamethis.props.userAge 来调用;

mapDispatchToProps

上面的例子中,定义了两个方法 adddec,两个方法分别使用 dispatch 分发加减操作的 action,在展示组件中使用 this.props.add()this.props.dec() 即可调用 store 去分发 action

step3: 渲染页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React from 'react';
import {render} from 'react-dom';
import { createStore } from 'redux';
import {Provider} from 'react-redux';
import reducer from './reducer.js';
import ContainerApp from './ContainerApp.js';
var store = createStore(reducer);
render(<Provider store={store}>
<ContainerApp/>
</Provider>, document.getElementById('root'));

React-Redux 提供了一个 Provider 组件,把 createStore() 创建的 store 对象传递给该组件,该 Provider 的所有子组件都能获取到 store 对象;

Over

使用 connect 将定义好的 mapStateToPropsmapDispatchToProps 把展示组件和容器组件连接起来,就不需要我们手动去使用 store.subscribe() 来添加监听去处理状态更新了。