redux的data flow
redux是JavaScript的状态容器, 通过redux, 我们可以轻松的对状态进行管理.
redux的data flow如下所示:
(图片来源于慕课网)
store存储着状态, 要想改变store必须使用action, store进行更新时使用reducer. 那么store, action, reducer是啥呢?
store和action都是对象, action中必须有一个type字段对进行的操作进行说明, 可能会有数据. store会赋值给组件中的state
reducer是一个纯函数, 接收两个参数, 第一个参数是累积对象(即state), 第二个参数就是action. reducer函数根据action.type的不同对state进行操作, 最后返回一个新的state, 这个新的state同时又是下一次的累积对象.
从图中可以看书store.dispatch(action)会要求改变store.
案例
我通过一个简单的加减一来描述state是如何更新的.
假设我们已经使用create-react-app脚手架工具创建好了项目目录. 保留src/目录中的index.js和App.js文件, src/目录下的其他文件全部删除.
index.js是程序的入口文件, 而App.js是具体内容挂载到public/index.html里id为"root"的元素中.
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
同时用下面的代码覆盖原来的App.js文件.
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Button } from 'antd'
class App extends Component {
constructor(props) {
super(props)
this.state = {
number: 1
}
}
render() {
return (
<div>
<div className="App" style={{marginLeft: '10px', marginBottom: '20px'}}>
<div>{this.state.number}</div>
<Button type="primary" >
+
</Button>
<Button>
-
</Button type="primary" >
</div>
</div>
)
}
}
export default App;
这里我们使用了antdUI组件库, 因此注意先安装.
打开命令行执行命令npm start
后显示如下:
我想实现这样一个需求, 当点击加号时, 数字加1, 点击减号时, 数字减一. 由于我们使用redux来管理状态, 那么就先引入redux, 并创建一个store.
import { createStore } from 'redux'
//store
const store = createStore()
再创建一个reducer.
//reducers
const defaultState = {
number: 1
}
function reducer(state = defaultState, action) {
//some codes
return state
}
我们使用defaultState来初始化数据. 并将reducer传递给store.createStore作为参数, 这样strore就会根据reducer来初始化值了. 这个值就是defaultState. reducer返回的新state会传给store, store再通过subscribe来触发更新
const store = createStore(reducer)
然后将App组件里constructor中的state使用store来初始化.
class App extends Component {
constructor(props) {
super(props)
this.state = store.getState()
store.subscribe(this.handleUpdate.bind(this))
}
render() {
return (
//some codes
)
}
handleUpdate() {
this.setState(store.getState())
}
}
export default App;
我们先对加号进行操作, 对其绑定点击事件.
//App.js
<Button
type="primary"
onClick={this.handleAdd.bind(this)}
>
+
</Button>
handleAdd() {
const action = {
type: 'add_number',
}
store.dispatch(action)
}
function reducer(state = defaultState, action) {
if (action.type === 'add_number') {
console.log('add_number')
const newState = JSON.parse(JSON.stringify(state))
newState.number += 1
return newState
}
return state
}
在handleAdd中定义了action, 使用store.dispatch(action)来调度. 当执行dispatch时, 组件的state对象和action对象就会传到reducer中并执行reducer. 我们在reducer函数定义了action.type为’add_number’时的情况.
我们点击加号, 返现数字加1了.
同理对于减号也是一样的.
这里我们直接将App.js的所有代码贴出来:
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Button } from 'antd'
import { createStore } from 'redux'
//reducer
const defaultState = {
number: 1
}
function reducer(state = defaultState, action) {
if (action.type === 'add_number') {
const newState = JSON.parse(JSON.stringify(state))
newState.number += 1
return newState
}
if (action.type === 'sub_number') {
const newState = JSON.parse(JSON.stringify(state))
newState.number -= 1
return newState
}
return state
}
//store
const store = createStore(reducer)
class App extends Component {
constructor(props) {
super(props)
this.state = store.getState()
store.subscribe(this.handleUpdate.bind(this))
}
render() {
return (
<div>
<div className="App" style={{marginLeft: '10px', marginBottom: '20px'}}>
<div>{this.state.number}</div>
<Button
type="primary"
onClick={this.handleAdd.bind(this)}
>
+
</Button>
<Button
type="primary"
onClick={this.handleSub.bind(this)}
>
-
</Button>
</div>
</div>
)
}
handleUpdate() {
this.setState(store.getState())
}
handleAdd() {
const action = {
type: 'add_number',
}
store.dispatch(action)
}
handleSub() {
const action = {
type: 'sub_number',
}
store.dispatch(action)
}
}
export default App;