redux
基础
概念
- redux是一个专门用于做状态管理的JS库(不是react插件库)
- 它可以用在react, angular, vue等项目中, 但基本与react配合使用
- 作用: 集中式管理react应用中多个组件共享的状态
作用
- 某个组件的状态,需要让其他组件可以随时拿到(共享)
- 一个组件需要改变另一个组件的状态(通信)
- 总体原则:能不用就不用, 如果不用比较吃力才考虑使用
工作流程

action
- 动作的对象
- 包含2个属性
- type:标识属性, 值为字符串, 唯一, 必要属性
- data:数据属性, 值类型任意, 可选属性
- 示例代码
{ type: 'ADD_STUDENT', data: {name: 'tom', age: 18} }
reducer
- 用于初始化状态、加工状态
- 加工时,根据旧的
state
和action
, 产生新的state
的纯函数
function reduxer(preState = 12, action) {
console.log(preState, action)
const { type, data } = action
switch (type) {
case 'add':
return preState + data
case 'decrement':
return preState - data
case 'chen':
return preState * data
case 'chu':
return preState / data
default:
return preState
}
}
export default reduxer
store
- 将state、action、reducer联系在一起的对象
- 如何得到此对象?
import {createStore} from 'redux'
import reducer from './reducers'
const store = createStore(reducer)
import { createStore } from 'redux'
import reduxer from './reduxer'
const store = createStore(reduxer)
export default store
- 此对象的功能?
- getState(): 得到state
- dispatch(action): 分发action, 触发reducer调用, 产生新的state。注意:他只负责返回新的 state,不负责页面更新,因此此时页面还是旧数据
- subscribe(listener): 注册监听, 当产生了新的state时, 自动调用
import React, { Component } from 'react'
import store from '../redux/store'
export default class Count extends Component {
componentDidMount() {
store.subscribe(() => {
this.setState({})
})
}
addFn = () => {
const { value } = this.selectNum
store.dispatch({ type: 'add', data: value * 1 })
}
addOddFn = () => {
const { value } = this.selectNum
const count = store.getState()
if (count % 2 !== 0)
store.dispatch({ type: 'add', data: value * 1 })
}
addAsyncFn = () => {
const { value } = this.selectNum
setTimeout(() => {
store.dispatch({ type: 'add', data: value * 1 })
}, 500)
}
render() {
return (
<div>
<h1>当前和为:{store.getState()}</h1>
<select ref={c => this.selectNum = c}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.addFn}>+</button>
<button onClick={this.addOddFn}>奇数加</button>
<button onClick={this.addAsyncFn}>异步加</button>
</div>
)
}
}
redux的核心API
createstore()
作用:创建包含指定 reducer
的 store
对象
store对象
- 作用: redux库最核心的管理对象
- 它内部维护着:
- state
- reducer
- 核心方法:
- getState()
- dispatch(action)
- subscribe(listener)
- 具体编码:
- store.getState()
- store.dispatch({type: 'INCREMENT', number})
- store.subscribe(render)
applyMiddleware()
作用:应用上基于 redux
的中间件(插件库)
使用:
- 引入插件
yarn add redux-thunk
- 引入
applyMiddleware
方法与redux-thunk
方法
import { applyMiddleware } from 'react'
import thunk from 'redux-thunk'
- 注册方法
export default createStore(redux, applyMiddleware(thunk))
- 创建异步的方法
export function createAction(data, time) {
return (dispatch) => {
setTimeout(() => {
dispatch({ type: 'xxx', data })
}, time)
}
}
官方文档说了,开启了中间件后,dispatch()
如果发现 action
是一个函数,会帮你封装。上方的代码中,刚好 return
返回一个函数,因此无需手动调用 store
。
异步 action
不是必须要用的。
- 使用
useActionFn = () => {
store.dispatch(createAction(1, 500))
}
combineReducers()
作用:合并多个reducer函数
react-redux
理解
- 一个
react
插件库 - 专门用来简化react应用中使用
redux
模型图
- 所有的 UI 组件都应该被一个容器组件包裹,他们是父子关系
- 容器组件真正和
redux
打交道,里面可以使用redux
的 API - UI 组件中不能使用
redux
的 API - 容器组件会传给 UI 组件以下数据:
redux
中保存的状态- 用于操作的状态
- 容器给 UI 传递状态、操作状态的方法,均通过
props
传递
分类
react-Redux
将所有组件分成两大类
- UI组件
- 只负责 UI 的呈现,不带有任何业务逻辑
- 通过
props
接收数据(一般数据和函数) - 不使用任何 Redux 的 API
- 一般保存在
components
文件夹下
- 容器组件
- 负责管理数据和业务逻辑,不负责UI的呈现
- 使用 Redux 的 API
- 一般保存在
containers
文件夹下
相关API
- Provider:让所有组件都可以得到state数据
- connect:用于包装 UI 组件生成容器组件
- mapStateToprops:将外部的数据(即state对象)转换为UI组件的标签属性
- mapDispatchToProps:将分发action的函数转换为UI组件的标签属性
容器组件的创建
- 安装
react-redux
yarn add react-redux
- 引入之前的组件(现在是作为 UI 组件)
import CountUI from '../../components/Count'
- 引入
connect
方法,连接 UI 组件与redux
import { connect } from 'react-redux'
- 使用
const CountContainer = connect()(CountUI)
上方代码看出两个信息:
connect
是一个函数- 其返回值也是一个函数
建立联系固定写法是在返回的函数中传参需要连接的 UI 组件。
- 导出
export default CountContainer
总体代码:
import { connect } from 'react-redux'
import CountUI from '../../components/Count'
const CountContainer = connect()(CountUI)
export default CountContainer
容器组件的连接
容器组件需要使用 store
,但是不能自己直接引入,而是要在父组件传入来,代码如下:
import React, { Component } from 'react'
import Count from '../comtainers/Count'
import store from '../redux/store'
export default class App extends Component {
render() {
return (
<div>
<Count store={store} />
</div>
)
}
}
容器组件传递的 store
给 UI 组件使用,该如何实现呢?前面组件传值都是 <A a="1" />
的 key
和 value
形式传参,但是 UI 组件这里不适用,需要通过函数的返回值作为状态传递给 UI 组件,代码如下:
import { connect } from 'react-redux'
import CountUI from '../../components/Count'
function mapStateToProps() {
return { key: 'value' }
}
function mapDispatchToProps() {
return {
fn: () => console.log(1),
func: () => console.log(2)
}
}
const CountContainer = connect(mapStateToProps, mapDispatchToProps)(CountUI)
export default CountContainer
返回的 key
作为传递给 UI 组件 props
的 key
,value
作为 UI 组件 props
的 value
——状态。
返回的 key: fn
作为传递给 UI 组件的 key
,作为 UI 组件 props
的 value
——操作状态的方法。
此时去往 UI 组件打印 this.props
,能够接收到一个对象,其中包含 APP 组件传递的 store
、connect
连接时接收的对象 key
和方法 fn
。
在函数中如果想要使用 store
内的变量,只需要通过形参的方式接收状态 state
即可。代码如下所示:
function mapStateProps(state) {
return { key: state }
}
UI 组件调用函数方法时,就触发传过去的函数的回调。因此 UI 组件调用方法传参,在 return
的回调函数中通过形参接收。函数中可以直接获取操作状态 dispatch
,代码如下:
function mapDispatchToProps(dispatch) {
return {
fn: number => dispatch({ type: 'add', data: number }),
func: number => dispatch({ type: 'lose', data: number }),
}
}
坑
- 如果状态不传对象而传其他类型,会报错,提示你需要返回一个对象
- 如果父组件不通过
store={store}
传递store
,运行后会报错没有store
代码优化
简写mapDispatchToProps
connect
方法中可以直接传函数,代码如下:
export default connect(
state => ({ count: state }),
dispatch => ({
fn: num => dispatch({ type: 'add', data: num })
})
)(CountUI)
其中,第二个参数又可以省略,不用传一个函数,直接传一个对象,代码如下:
export default connect(
state => ({ count: state }),
{
fn: data => ({ type: 'add', data })
}
)(CountUI)
原理:
对象这个写法,相当于为 UI 组件的 props
传递一个名为 fn
的 action
函数,UI 组件调用 fn
后实际上就是调用 data => ({type: 'add', data})
这个箭头函数。
然后好像在这里就停住了,他无法调用 dispatch
。这里我们只需要做到这里就好了,react-redux
在底层帮我们处理了,我们只需要给他一个 action
函数,他底部会帮我们调用 dispatch
。
Provider组件的使用
在使用 redux
时,需要使用 store.subscribe()
监测变化更新视图,代码如下:
store.subscribe(() => {
ReactDOM.render(<App/>, document.getElementById('root'))
})
使用了 react-redux
后,我们不再需要 store.subscribe()
监测也能实现效果了。原理是容器组件是通过 connect()()
创建建立连接的,因此底部已经检测了,不需要我们再监测了。
前面使用一个容器组件时,通过为标签添加 store={store}
实现传递 store
。但是如果多个容器组件时,一个一个写显然不太合理,可以直接使用 Provider
方法统一传递 store
对象,代码如下:
<Provider store={store}>
<App />
</Provider>
整合组件
UI 组件和容器组件分开两个文件后期容易文件冗余不好维护,把两个组件整合到一起作为一个组件。UI 组件无需暴露,容器组件与 UI 组件建立连接后暴露容器组件即可。代码如下所示:
import React, { Component } from 'react'
import { connect } from 'react-redux'
class Count extends Component {
handleAddFn = () => {
this.props.add(5)
}
render() {
return (
<>
<div>当前求和为:{this.props.count}</div>
<button onClick={this.handleAddFn}>加一</button>
</>
)
}
}
export default connect(
state => ({ count: state }),
{
add: data => ({ type: 'add', data })
}
)(Count)
数据共享
如果有多个 reducer
需要共享数据,上面的方法显然不合适,他只能挂载一个 reducer
。要如何挂载多个状态呢?我们都知道,js 中能挂载多个变量的方法只有数组和对象,而相对而言,对象是更优于数组的。
这个时候需要使用到 combineReducers
,combine
翻译成中文就是合并,其是一个函数,往里面传一个对象,代码如下:
import { applyMiddleware, combineReducers, createStore } from 'redux'
import count_redux from './reduxer'
import person_reduxer from './reduxerperson'
const a = combineReducers({
pre: person_reduxer,
count: count_redux
})
const store = createStore(a)
export default store
现在,store
的状态就有了两个属性可以使用,运行一下看看效果,发现报错,报错信息如下所示:

在 Count
组件中,我们写了这么一段代码 (state) => ({count: state})
。在之前没有做数据共享状态合并前他只是一个普通的变量,因此可以直接使用。而现在做了共享合并后,state
已经是合并状态了,即是 {pre: person_reduxer, count: count_redux}
。因此在容器组件中需要通过对象点语法来获取。代码如下:
export default connect(
state => ({ count: state.count }),
{
add: data => ({ type: 'add', data })
}
)(Count)
如果想要使用其他 reducer
内的变量可以在 state
中获取,直接在 state
箭头函数中返回的对象内添加即可。
纯函数
...(about 272 lines omitted)...