redux是React的状态管理工具,它的工作流程如下
Redux适合的场景
- 用户的使用方式复杂
- 不同身份的用户有不同的使用方式
- 用户之间可以协作
- 与服务器大量交互
- view要从多个来源获取数据
安装redux
yarn add redux
基本概念
Store
保存数据的地方,整个应用只能有一个Store。创建Store的方法为createStore
import { createStore } from 'redux'
import reducer from './reducer'
const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
export default store
State
Store 包含所有的数据,State 是某一个时间点的数据集合,获取State的方法为store.getState()
this.state = store.getState()
Action
Action 用来改变Store中的数据,action
是一个对象,约定必须有一个type
属性,它表示action
的名称,其他属性可以任意设置
handleInputChange(e) {
const action = {
// action 的类型
type: CHANGE_INPUT_VALUE,
// 需要传递的参数
value: e.target.value
}
// 调用 dispatch 方法修改 store 的值
store.dispatch(action)
}
Reducer
Reducer接受action
修改Store的请求,然后把修改后的State返回给Store,Store用Reducer返回的值替换原来的值,因此只有Store能够改变自己的内容。
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'
const defaultState = {
inputValue: '',
list: []
}
// reducer 可以接收 state,但不能修改 state
const fn = (state = defaultState, action) => {
if (action.type === CHANGE_INPUT_VALUE) {
const newState = JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
return newState
}
return state
}
export default fn
Reducer必须是一个纯函数,纯函数满足以下两点
- 给定输入输出是固定的
const fn = (state = defaultState, action) => {
if (action.type === CHANGE_INPUT_VALUE) {
const newState = JSON.parse(JSON.stringify(state))
// inputValue 受到当前时间影响,所以fn不是纯函数
newState.inputValue = new Date()
return newState
}
return state
}
- 没有副作用
const fn = (state = defaultState, action) => {
if (action.type === CHANGE_INPUT_VALUE) {
const newState = JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
// 对传入参数进行修改,因此是有副作用的
state.inputValue = ''
return newState
}
return state
}
Action Creator
一个项目可能会用到很多action,可以众多action集中到一个文件,便于进行管理
store/actionCreators.js
import { ADD_TODO_ITEM, CHANGE_INPUT_VALUE, DELETE_TODO_ITEM } from './actionTypes'
export const getInputChangeAction = (value) => ({
type: CHANGE_INPUT_VALUE,
value
})
Redux高级
UI组件和容器组件
将React组件划分为UI组件和容器组件,UI组件负责渲染,容器组件负责处理逻辑,这样的划分可以避免组件的代码过于庞大,也让代码逻辑更加清晰。
TodoList.js
import React, { Component } from 'react'
import 'antd/dist/antd.css'
import store from './store'
import TodoListUI from './TodoListUI'
import { getInputChangeAction, getAddItemAction, getDeleteItemAction } from './store/actionCreators'
class TodoList extends Component {
constructor(props) {
super(props)
this.state = store.getState()
this.handleInputChange = this.handleInputChange.bind(this)
this.handleStoreChange = this.handleStoreChange.bind(this)
this.handleBtnClick = this.handleBtnClick.bind(this)
this.handleItemDelete = this.handleItemDelete.bind(this)
}
componentDidMount() {
// 订阅 store 的变化,交给 handleStoreChange 处理
store.subscribe(this.handleStoreChange)
}
componentWillUnmount() {
store.unsubscribe(this.handleStoreChange) // 取消订阅,清理已注册的监听
}
render() {
return (
)
}
handleInputChange(e) {
const action = getInputChangeAction(e.target.value)
// 调用 dispatch 方法修改 store 的值
store.dispatch(action)
}
handleStoreChange() {
// 根据 store 更新 state
this.setState(store.getState())
}
handleBtnClick() {
const action = getAddItemAction()
store.dispatch(action)
}
handleItemDelete(index) {
const action = getDeleteItemAction(index)
store.dispatch(action)
}
}
export default TodoList
TodoListUI.js
import React, { Component } from 'react'
import { Input, Button, List } from 'antd'
class TodoListUI extends Component {
render() {
return (
TODO
(
// onClick 的监听函数需要接收参数,应该修改为函数的形式
{
this.props.handleItemDelete(index)
}}
key={index}
>
{item}
)}
/>
)
}
}
export default TodoListUI
store/actionCreators.js
import { ADD_TODO_ITEM, CHANGE_INPUT_VALUE, DELETE_TODO_ITEM } from './actionTypes'
export const getInputChangeAction = (value) => ({
type: CHANGE_INPUT_VALUE,
value
})
export const getAddItemAction = () => ({
type: ADD_TODO_ITEM
})
export const getDeleteItemAction = (index) => ({
type: DELETE_TODO_ITEM,
index
})
无状态组件
无状态组件指的是只含有render函数的组件,一般可以将UI组件可以改写成无状态组件,可以提升一部分性能。
import React, { Component } from 'react'
import { Input, Button, List } from 'antd'
const TodoListUI = (props) => {
return (
TODO
(
// onClick 的监听函数需要接收参数,应该修改为函数的形式
{
props.handleItemDelete(index)
}}
key={index}
>
{item}
)}
/>
)
}
export default TodoListUI
Redux发送异步请求获取数据
yarn add axios
TodoList.js
componentDidMount() {
// 订阅 store 的变化,交给 handleStoreChange 处理
store.subscribe(this.handleStoreChange)
axios.get('http://localhost.charlesproxy.com:3000/api/todolist').then((res) => {
const data = res.data
const action = initListAction(data)
store.dispatch(action)
})
}