标签:计数器 turn 操作 button 副本 有用 同步 运算符 sub
在大型项目中,如果完全使用react管理数据将会是一件很麻烦的事情。这一章,我们将介绍如何使用redux来管理你的应用状态。
我们知道,react的数据都是“单向数据流”,即由父组件往子组件一层一层的传递。redux在此基础上又强调了三个基本原则:
整个应用只有一个store,所有组件的数据源就是这个store上的状态。这句话怎么理解呢?比如,我们要做一个博客系统,这个博客系统只有用户信息模块和显示博客列表模块。那么,store上的状态应该像下面这样设计:
initState = {
userInfo: ...,
blogList: ...
}
initState.userInfo用来存储用户信息,initState.blogList用来存储博客信息。这样,我们就可以在一个store上存储所有的应用信息。
不可直接修改状态,要通过派发一个action对象去完成修改。关于action,后面再做讲解。
这里所说的純函数指的是就是reducer。那么何为純函数呢?純函数指的就是返回结果必须完全由参数决定,且不得修改参数值。
reducer只负责计算state,并不负责存储state。存储state的工作交给Redux框架去做。在这里,你只需要知道当我们派发了一个action后,redux会根据action的type值,在reducer找到对应的条件分支,然后去处理这个分支下的行为,state的计算就在这个条件分之下完成。
下面,我们将创建一个简单的计数器应用来讲解具体该如何使用redux。应用界面的渲染必须通过state或props的改变来触发,而state或props的改变是用户操作行为导致的结果。上面说过,要修改状态需要派发一个action,这个action的派发,就是用户行为导致的。
action顾名思义代表一个“动作”,不过这个动作是一个普通的javascript对象。action对象必须有一个名为type的字段,代表这个action对象的类型。比如,计数器有加减操作,那么action的type可定义为‘increment‘或‘decrement‘。
定义action通常需要两个文件,一个用于保存action的类型,一个用于创建action的构造函数。
在ActionTypes.js文件中,我们定义action的类型。
// ActionTypes.js
export const INCREMENT = ‘increment‘;
export const DECREMENT = ‘decrement‘;
上面分别定义了用户的两个动作,加和减。
我们在actions.js中定义action的构造函数。注意,创建的是action构造函数,而不是action本身,action构造函数会返回一个action。
// Actions.js
import * as ActionTypes from ‘./ActionTypes.js‘;
export const increment = (counterCaption) => {
return {
type: ActionTypes.INCREMENT,
counterCaption: counterCaption
};
};
export const decrement = (counterCaption) => {
return {
type: ActionTypes.DECREMENT,
counterCaption: counterCaption
};
};
action创建了之后,我们需要将它派发出去,以此来改变state。上面已经介绍过,state被存储在store,store提供dispatch()方法来派发action,dispatch接收action对象作为参数。
我们先创建一个store.js文件,输出全局唯一的那个store。
// Store.js
import {createStore} from ‘redux‘;
import reducer from ‘./Reducer.js‘;
const initValues = {
‘First‘: 0,
‘Second‘: 10,
‘Third‘: 20
};
const store = createStore(reducer, initValues);
export default store;
redux提供createStore方法来创建store。createStore方法接收两个参数,第一个参数代表更新状态的reducer,第二个参数是状态的初始值。关于createStore的具体用法,后面章节会介绍,这里只要知道这两个参数就行。
接下来创建reducer函数,reducer用于计算state。
// Reducer.js
import * as ActionTypes from ‘./ActionTypes.js‘;
export default (state, action) => {
const {counterCaption} = action;
switch (action.type) {
case ActionTypes.INCREMENT:
return {...state, [counterCaption]: state[counterCaption] + 1};
case ActionTypes.DECREMENT:
return {...state, [counterCaption]: state[counterCaption] - 1};
default:
return state
}
}
在reducer函数中,根据action.type的判断条件来执行state的计算。上面的代码,我们使用了扩展运算符(...),不懂的同学自行补脑吧,在es6的课程有讲解。
记住:reducer中绝对不能直接修改state。因为reducer是一个純函数,純函数的返回结果完全由传入的参数决定,且不可修改参数值。 使用扩展运算符,实际上复制了一个state副本,操作的是这个副本,犹如jquery中$.extend()的用法。
接下来,我们看一下view部分。view中有三个组件,最外层的是ControlPanel组件。
// ControlPanel.js
class ControlPanel extends Component {
render() {
return (
<div style={style}>
<Counter caption="First"/>
<Counter caption="Second"/>
<Counter caption="Third"/>
<hr/>
<Summary/>
</div>
);
}
}
ControlPanel组件中包含Counter和Summary两个组件,这里主要介绍Counter组件的写法,Summary组件和Counter组件写法差不多。
首先初始化this.state
// Counter.js
class Counter extends Component {
constructor(props) {
super(props);
...
this.state = this.getOwnState();
}
getOwnState() {
return {
value: store.getState()[this.props.caption]
};
}
...
}
我们把从store上获取状态的逻辑放在getOwnState()方法中,store.getState()可获得当前状态,也就是我们在Store.js中定义的initValues。
为了保证store上的状态和this.state保持同步,需要监听store的变化。
onChange() {
this.setState(this.getOwnState());
}
componentDidMount() {
store.subscribe(this.onChange);
}
componentWillUnmount() {
store.unsubscribe(this.onChange);
}
在componentDidMount中。通过store.subscribe监听store变化,只要store发生变化,就会调用onChange方法;在componentWillUnmount函数中,我们把这个监听注销,和componentDidMount中的动作对应。
改变store中状态的唯一方法是派发一个action。如下:
// Counter.js
class Counter extends Component {
...
onIncrement() {
store.dispatch(Actions.increment(this.props.caption));
}
onDecrement() {
store.dispatch(Actions.decrement(this.props.caption));
}
...
render() {
const value = this.state.value;
const {caption} = this.props;
return (
<div>
<button style={buttonStyle} onClick={this.onIncrement}>+</button>
<button style={buttonStyle} onClick={this.onDecrement}>-</button>
<span>{caption} count: {value}</span>
</div>
);
}
}
store.dispatch()方法派发一个action后,redux会根据action的type值,查找在reducer函数中相符的判断条件来执行state的计算。
标签:计数器 turn 操作 button 副本 有用 同步 运算符 sub
原文地址:https://www.cnblogs.com/renzhiwei2017/p/9480910.html