参考资料
Redux = Reducer + Flux
调试Redux:chrome插件Redux DevTools
纯函数
纯函数指的是,给定固定的输入,就一定会返回固定的输出(所以不能有ajax请求、new Date()和定时器)。而且不会有任何副作用(如不能修改store的内容,要先复制一份再去操作)。
Redux设计和使用的三项原则
-
store是唯一的
-
只有store能够改变自己的内容
-
Reducer必须是纯函数
Redux的核心API
- createStore: 创建store仓库
- store.dispatch: 派发action,并传递给store
- store.getState: 获取store中所有的内容
-
store.subscribe: 订阅store的改变
Redux 的工作流程
-
React Components是借阅人,要借书
-
Store是用来存储数据的. 相当于图书管理员
-
Action就是借阅人对图书管理员说的话(我要借xx书)
-
Reducers就是借阅记录,管理员在这里找书
用一个实际的例子来解释redux的工作流程:
当借阅人(React Components)要借书时,对管理员(Store)说”我要借xx书”(Action),管理员就在借阅记录(Reducers)上找出来。然后将书(state)给借阅人(React Components)。
Redux的实际工作流程
上图为流程图,下面为简单的代码实现逻辑
src/store/index.js
const store = createStore(reducer) // 2 Store依赖reducer存储和更新数据
src/store/reducer.js
export defalut (state, action) => {
return newState // 5 处理数据并return state给Store
return state // 1 初始化数据并return state给Store
}
src/TodoList.js
constructor(props) {
super(props)
this.state = store.getState() // 3 获取数据
store.subscribe(this.handleStoreChange) // 6 订阅store,监听store中state的变化
}
render() {
return (<button onClick={this.handleSubmit.bind(this)}><button>
}
handleSubmit() {
const action = {
type: '',
value: ''
}
store.dispatch(action) // 4 修改数据并转发给Store
}
handleStoreChange() { // 7 监听到Store变化并更新组件
this.setState(store.getState())
}
-
要想更新state中的数据,首先派发(dispatch)一个action,action通过dispatch方法把类型(type)和新数据传给store。
-
store把之前的数据(previousState)和传过来的action转发给reducers函数。
-
reducers接收previousState和action后进行数据处理,重新生成一个newState(原state只读不改),把newState作为返回值返回给store。
-
store接收newState,将新数据替换原来的数据。
-
react组件中观测(store.subscribe)到数据发生改变,会从store里面重新取数据(state)设置(setState)state,更新组件的内容,页面发生变化。
在项目中使用Redux
Installation
npm install --save redux
Store文件目录
- src/store/actionCreators.js 统一管理action
- src/store/actionTypes.js action的type定义成常量利于定位错误
- src/store/index.js
- src/store/reducer.js
- src/index.js
- src/TodoList.js
src
store
actionCreators.js
// src/store/actionCreators.js
import { CHANGE_INPUT_VALUE, BTN_SUBMIT, DELETE } from './actionType'
export const getInputChangeAction = (value) => ({
type: CHANGE_INPUT_VALUE,
value
})
export const getBtnSubmitAction = () => ({
type: BTN_SUBMIT
})
export const getDeleteAction = (index) => ({
type: DELETE,
index
})
actionTypes.js
// src/store/actionTypes.js
export const CHANGE_INPUT_VALUE = 'change_input_value'
export const BTN_SUBMIT = 'btn_submit'
export const DELETE = 'delete'
index.js
// src/store/index.js
// 1 store仓库建立
import { createStore } from 'redux'
import reducer from './reducer' // 2 import reducer的数据
// redux devtools
const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION_ && window.__REDUX_DEVTOOLS_EXTENSION_())
export default store // export store
reducer.js
// src/store/reducer.js
// 2 reducer建立 export出state
import { CHANGE_INPUT_VALUE, BTN_SUBMIT, DELETE } from './actionType'
const defaultState = { // store默认值
inputValue: '',
list:[]
}
// reducer 可以接收state 但是绝不能修改state
// 纯函数指的是,给定固定的输入,就一定会有固定的输出,而且不会有任何副作用
export default (state = defaultState, action) => { // 5 store接收组件传来的action解析,并修改store中的state
if(action.type === CHANGE_INPUT_VALUE) {
const newState = JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
return newState
}
if(action.type === BTN_SUBMIT) {
const newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputValue)
newState.inputValue = ''
return newState
}
if(action.type === DELETE) {
const newState = JSON.parse(JSON.stringify(state))
newState.list.splice(action.index, 1)
return newState
}
return state
}
index.js
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import TodoList from './TodoList'
ReactDOM.render(<TodoList />, document.getElementById('root'))
TodoList.js
// src/TodoList.js
import React, { Component, Fragment } from 'react'
import store from './store' // 3 引入store供组件使用
import { getInputChangeAction, getBtnSubmitAction, getDeleteAction } from './store/actionCreators'
// import { CHANGE_INPUT_VALUE, BTN_SUBMIT, DELETE } from './store/actionType'
import 'antd/dist/antd.css'
import { Input, Button, List } from 'antd'
class TodoList extends Component {
constructor(props) {
super(props)
this.state = store.getState() // 3 引入store赋值给组件的state
this.handleInputChange = this.handleInputChange.bind(this)
this.handleBtnClick = this.handleBtnClick.bind(this)
this.handleStoreChange = this.handleStoreChange.bind(this)
store.subscribe(this.handleStoreChange) // 6 订阅store,监听store中state的变化
}
render() {
return (
<Fragment>
<div style={{marginTop: '10px', marginLeft: '10px'}}>
<Input
placeholder='todo info'
style={{width: '300px', marginRight: '10px', marginBottom: '10px'}}
value={this.state.inputValue}
onChange={this.handleInputChange}
/>
<Button onClick={this.handleBtnClick} type="primary">提交</Button>
<List
style={{width: '300px'}}
bordered
dataSource={this.state.list}
renderItem={(item, index) => (
<List.Item onClick={this.handleDelete.bind(this, index)}>{item}</List.Item>
)}
/>
</div>
</Fragment>
)
}
handleInputChange(e) { // 4 组件的state值发生改变,存储到action,并通过store.dispatch(action)转发给store
const action = getInputChangeAction(e.target.value)
store.dispatch(action)
}
handleBtnClick() {
const action = getBtnSubmitAction()
store.dispatch(action)
}
handleDelete(index) {
const action = getDeleteAction(index)
store.dispatch(action)
}
handleStoreChange() { // 7 监听到store的变化,更新当前组件的state
this.setState(store.getState())
}
}
export default TodoList