redux
简介
redux
是一个专门用于做状态管理的JS
库- 它可以用在
react
,angular
,vue
等项目中,但基本与react
配合使用 - 作用:集中式管理
react
应用中多个组件共享但状态
什么场景下需要使用redux
- 某个组件的状态,需要让其他组件可以随时拿到
(共享)
- 一个组件需要改变另一个组件的状态
(通信)
- 总体原则:能不用就不用,如果不用比较吃力才考虑使用
redux的工作流程
三个核心概念
Action Creators
action
:动作对象,包含两个属性
type
:动作类型data
:数据
dispatch
:分发,将action
对象向下传递
- 用法:
dispatch(action)
Store
将state
、action
、reducer
联系在一起的对象,通俗的说就是自己本身不做处理,接收到动作后,调用Reducers
做具体的状态加工。
它会传递两个参数给Reducers
。
- previousState:前一个状态
- action:动作
-
如何得到该对象?
import {createStore} from 'redux'
import reducer from './reducers'
const store = createStore(reducer)
-
此对象的功能
getState()
:得到state
dispatch(action)
:分发action
,触发reducer
调用,产生新的state
subscribe(listener)
:注册监听,当产生新的state
时,自动调用
Reducers
- 初始化状态
- 加工状态
- 加工时,根据旧的
state
和action
,产生新的state
的纯函数
- 加工时,根据旧的
案例演示
需求:实现一个包含加减乘除的简易版计算器
通过实现该案例来掌握redux
的核心API。
基础使用版本
基础代码片段
import React, { Component } from 'react'
export default class Count extends Component {
state = { count: 0 }
render() {
return (
<div>
<h1>当前求和为:{this.state.count}</h1>
<select ref={c => this.selectNumber = c}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>求和结果为奇数再增加</button>
<button onClick={this.incrementAsync}>异步增加</button>
</div>
)
}
}
基础的东西有了,接下来我们再先快速的实现一下几个按钮的功能吧。
// 加法
increment = () => {
const { value } = this.selectNumber
const { count } = this.state
this.setState({ count: count + value * 1 })
}
// 减法
decrement = () => {
const { value } = this.selectNumber
const { count } = this.state
this.setState({ count: count - value * 1 })
}
// 求和结果为奇数再增加
incrementIfOdd = () => {
const { value } = this.selectNumber
const { count } = this.state
if (count % 2 !== 0) {
this.setState({ count: count + value * 1 })
}
}
// 模拟异步增加
incrementAsync = () => {
const { value } = this.selectNumber
const { count } = this.state
setTimeout(() => {
this.setState({ count: count + value * 1 })
}, 1000);
}
基础已经搭建好了,现在就准备接入Redux
吧。
- 安装
redux
yarn add redux
- 创建
store.js
// 引入 createStore,用于创建store对象
import { legacy_createStore as createStore} from 'redux'
// 引入为count组件服务的reducer
import countReducer from './count_reducer'
// 暴露store
export default createStore(countReducer)
这里需要注意的是新版中中
createStore
已经被弃用,需要使用legacy_createStore
来代替。
- 创建
count_reducer.js
// reducer 本质上一个函数,该文件主要用于创建一个为Count组件服务的reducer
const initState = 0
export default function countReducer(preState = initState, action){
const {type, data} = action
switch(type){
case 'increment':
return preState + data
case 'decrement':
return preState - data
default:
return preState
}
}
redux
相关的两个js定义好了,接下来就需要修改我们的Count
组件了。
- 首先引入
store
import store from '../../redux/store'
- 通过
getState()
API获取状态
<h1>当前求和为:{store.getState()}</h1>
- 修改
increment
方法
increment = () => {
const { value } = this.selectNumber
store.dispatch({
type: 'increment',
data: value * 1
})
}
在count_reducer.js
中打印一下,看到函数有被调用执行,但是页面显示到内容却没有发生变化。
我们再返回去看看redux
的工作流程,可以看到redux
只负责帮我们处理状态,而页面的更新实际上是React
做的事情,我们更新的是redux
中的状态,在React
这边并没有发生改变,所以页面自然也不会触发render()
去帮我们重新渲染。
在Count
组件中,我们加上勾子函数调用下redux
提供的订阅API,来触发下渲染看看行不行。
componentDidMount() {
// 通过订阅redux的状态变化,调用render重新渲染页面
store.subscribe(() => {
this.setState({})
})
}
// 加法
increment = () => {
const { value } = this.selectNumber
store.dispatch({
type: 'increment',
data: value * 1
})
}
// 减法
decrement = () => {
const { value } = this.selectNumber
store.dispatch({
type: 'decrement',
data: value * 1
})
}
// 求和结果为奇数再增加
incrementIfOdd = () => {
const { value } = this.selectNumber
if (store.getState() % 2 !== 0) {
store.dispatch({
type: 'increment',
data: value * 1
})
}
}
// 模拟异步增加
incrementAsync = () => {
const { value } = this.selectNumber
setTimeout(() => {
store.dispatch({
type: 'increment',
data: value * 1
})
}, 1000);
}
除了组件自身的勾子函数,我们也可以在index.js
文件中实现该功能,每次状态发生变化,重新渲染整个App
组件。
// 引入React核心库
import React from 'react'
// 引入ReactDOM
import { createRoot } from 'react-dom/client'
// 引入store
import store from './redux/store'
// 引入自定义组件
import App from './App.js'
// 渲染组件到页面
const container = document.getElementById('root')
const root = createRoot(container)
root.render(<App />)
store.subscribe(() => {
root.render(<App />)
})
React
中有diff算法,直接重新渲染整个App
组件,也不怕会有太大的效率问题发生。
完整使用版本
在上面的案例中,我们需要自己创建action
对象,而且对于type
的值也没有一个统一的处理,极容易出错导致状态变化无法识别到。在这里我们可以再定义两个文件:count_action.js
、constant.js
// 该文件专门为Count组件生产action对象
import {INCERMENT, DECERMENT} from './constant'
// 创建 incerment action对象
export const createIncrementAction = data => ({type: INCERMENT, data})
// 创建 decerment action对象
export const createDecrementAction = data => ({type: DECERMENT, data})
// 常量定义
export const INCERMENT = 'increment'
export const DECERMENT = 'decrement'
完整代码
import React, { Component } from 'react'
import store from '../../redux/store'
import {createIncrementAction, createDecrementAction} from '../../redux/count_action'
export default class Count extends Component {
state = {}
// 加法
increment = () => {
const { value } = this.selectNumber
store.dispatch(createIncrementAction(value * 1))
}
// 减法
decrement = () => {
const { value } = this.selectNumber
store.dispatch(createDecrementAction(value * 1))
}
// 求和结果为奇数再增加
incrementIfOdd = () => {
const { value } = this.selectNumber
if (store.getState() % 2 !== 0) {
store.dispatch(createIncrementAction(value * 1))
}
}
// 模拟异步增加
incrementAsync = () => {
const { value } = this.selectNumber
setTimeout(() => {
store.dispatch(createIncrementAction(value * 1))
}, 1000);
}
render() {
return (
<div>
<h1>当前求和为:{store.getState()}</h1>
<select ref={c => this.selectNumber = c}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>求和结果为奇数再增加</button>
<button onClick={this.incrementAsync}>异步增加</button>
</div>
)
}
}
异步action
上面案例展示的都是同步的action
,但是我们还有一个按钮是要发送异步请求的,接下来我们就来学习一下异步action
该如何创建。
安装redux-thunk
库
npm add redux-thunk
修改store.js
,引入applyMiddleware
和thunk
// 引入 createStore,用于创建store对象
import { legacy_createStore as createStore, applyMiddleware} from 'redux'
// 引入为count组件服务的reducer
import countReducer from './count_reducer'
// 引入 thunk 中间件
import thunk from 'redux-thunk'
// 暴露store
export default createStore(countReducer, applyMiddleware(thunk))
创建异步action
// 异步action,就是指action的值为函数
export const createIncrementAsyncAction = (data, time) => {
return (dispatch) => {
setTimeout(() => {
dispatch(createIncrementAction(data))
}, time)
}
}
核心API
store.getState()
store.subscribe()
相关文档
- 英文文档:redux.js.org/
- 中文文档:www.redux.org.cn/
- Github:github.com/reactjs/red…
本文正在参加「金石计划 . 瓜分6万现金大奖」