-
Redux
以数据存储中心Store
为核心,修改数据、初始化数据等通过Reducers
,Reducers
操作完成后会通过组件对Store
的订阅告知组件数据更新了,当然组件也可以发起对数据的修改,组件通过发送Action
,派发行为给Store
,然后通知Reducers
进行相应操作 -
Redux
是不存在异步操作的 - 在使用之前要先安装
Redux
,当前使用版本:
"redux": "^4.0.5"
- 举个计数器例子认识下最基本的
Redux
,在src
目录下创建一个store.js
// src/store.js
import {createStore} from 'redux';
// 初始化状态 state = 0
// 状态的操作
const counterReducer = (state = 0, action) => {
switch(action.type){
case 'add':
return state + 1;
case 'minus':
return state - 1;
default:
return state;
}
}
// 创建一个store
const store = createStore(counterReducer)
export default store;
- 然后来使用这个
store
,写到这的时候发现页面中的加减按钮不生效,原因是组件要实现对store
的操作,必须订阅store
的状态更新
// src/components/ReduxTest.js
import React from 'react';
// 引入store
import store from '../store'
class ReduxTest extends React.Component{
render () {
return (
<div>
{/* 获取状态 */}
<p>{store.getState()}</p>
{/* 通过dispatch派发action */}
<div>
<button onClick={() => store.dispatch({type: 'minus'})}>-</button>
<button onClick={() => store.dispatch({type: 'add'})}>+</button>
</div>
</div>
)
}
}
export default ReduxTest
- 在没有使用其他插件的情况下,我们对
index.js
添加部分内容来实现订阅,添加完成后计数器就生效了
// index.js
// 添加store.subscribe方法,在该方法中再次执行ReactDOM的渲染,实现对store的订阅
store.subscribe(() => {
ReactDOM.render(
// <React.StrictMode>
<App />,
// </React.StrictMode>,
document.getElementById('root')
);
})
- 最基本的
Redux
可以看出用起来还是比较繁琐的,每个组件都要引入store
、index.js
中还要频繁更新DOM
,为了解决这些问题可以使用相对更友好的插件库,这里安装下react-redux
,这个库主要提供了2个api
,分别是Provider
、connect
,安装完成后我们来对上面的例子做重构
"react-redux": "^7.2.0"
// 1. 首先去掉index.js中的订阅
// index.js
// 添加store.subscribe方法,在该方法中再次执行ReactDOM的渲染,实现对store的订阅
// store.subscribe(() => {
// ReactDOM.render(
// // <React.StrictMode>
// <App />,
// // </React.StrictMode>,
// document.getElementById('root')
// );
// })
// 2. 修改使用 ReduxTest.js 的方式
// App.js
import store from './store'
import {Provider} from 'react-redux'
{/* 上下文的形式隔代传递数据 */}
<Provider store={store}>
<ReduxTest></ReduxTest>
</Provider>
// 3. 重构计数器组件
// src/components/ReactTest.js
import React from 'react';
// connect函数连接 react-redux 和 redux
import {connect} from 'react-redux';
// 映射函数
const mapStateToProps = state => ({num: state});
// 简化store.dispatch的操作
const mapDispatchToProps = {
add: () => ({type: 'add'}),
minus: () => ({type: 'minus'})
}
// 装饰器写法:
@connect(mapStateToProps, mapDispatchToProps)
class ReduxTest extends React.Component{
render () {
const {num, add, minus} = this.props
return (
<div>
{/* 获取状态 */}
<p>{num}</p>
{/* 通过dispatch派发action */}
<div>
<button onClick={minus}>-</button>
<button onClick={add}>+</button>
</div>
</div>
)
}
}
export default ReduxTest
- 接着再看如何让
Redux
支持异步,需要中间件的支持,在Action
被派发至Store
之前先通过中间件的处理,在中间件处理中可以对Action
做很多操作,比如日志记录等,我们使用一个相对简单的中间件redux-thunk
,redux-thunk
实现异步的基本原理是判断当action
是个函数时,先执行这个函数,也同时安装一个redux-logger
用于日志记录,然后对计数器组件做一些变化
"redux-thunk": "^2.3.0"
"redux-logger": "^3.0.6"
// 1. 修改store.js
// src/store.js 修改以下部分内容,其他不变
// applyMiddleware 应用中间件方法
import {createStore, applyMiddleware} from 'redux';
// 引入中间件
import logger from 'redux-logger'
import thunk from 'redux-thunk'
// 创建一个store
const store = createStore(
counterReducer,
// 按照需要使用的中间件的顺序传递参数
applyMiddleware(logger, thunk)
)
// 2. 修改计数器组件,新增异步操作
// src/components/ReactTest.js 修改部分内容
// 新增asyncAdd异步操作
const mapDispatchToProps = {
add: () => ({type: 'add'}),
minus: () => ({type: 'minus'}),
// asyncAdd方法return一个方法
asyncAdd: () => dispatch => {
// 异步操作
setTimeout(()=> {
dispatch({type: 'add'})
},2000)
}
}
// 添加异步操作的对应按钮
<div>
<button onClick={minus}>-</button>
<button onClick={add}>+</button>
<button onClick={asyncAdd}>async +</button>
</div>
-
完成以上的支持异步的代码之后,我们来观察不同操作的日志情况,日志记录了方法名称、操作前后的值变化等
-
实现异步操作之后,来对
store.js
的部分做个架构分层,实际开发中可能会有多个Store
分管不同模块的数据,在src
目录下新建一个store
文件夹
// src/store/index.js
// applyMiddleware 应用中间件方法
import {createStore} from 'redux';
import logger from 'redux-logger'
import thunk from 'redux-thunk'
import {counterReducer} from './count.redux'
const store = createStore(
applyMiddleware(logger, thunk)
);
export default store;
// src/store/count.redux.js
export const counterReducer = (state=0, action) => {
switch(action.type){
case 'add':
return state + 1;
case 'minus':
return state - 1;
default:
return state;
}
}
// action creator
export const add = () => ({type: 'add'})
export const minus = () => ({type: 'minus'})
// asyncAdd方法return一个方法
export const asyncAdd = () => dispatch => {
// 异步操作
setTimeout(()=> {
dispatch({type: 'add'})
},1500)
}
- 修改好
store.js
的架构分层之后,下面就是使用了,接着修改在计数器组件中使用Store
的部分
import React from 'react';
import {connect} from 'react-redux';
// 将action creator导入
import {add, minus, asyncAdd} from '../store/count.redux'
const mapStateToProps = state => ({num: state});
// count.redux之后
const mapDispatchToProps = {add, minus, asyncAdd}
@connect(mapStateToProps, mapDispatchToProps)
class ReduxTest extends React.Component{
render () {
const {num, add, minus, asyncAdd} = this.props
return (
<div>
{/* 获取状态 */}
<p>{num}</p>
{/* 通过dispatch派发action */}
<div>
<button onClick={minus}>-</button>
<button onClick={add}>+</button>
<button onClick={asyncAdd}>async +</button>
</div>
</div>
)
}
}
export default ReduxTest
- 重构结构完成之后,继续
store
添加新的模块,实现下多个store
如何使用
// src/store/index.js
// 引入combineReducers
import {createStore, applyMiddleware, combineReducers} from 'redux';
const store = createStore(
// combineReducers将多个模块的reducer合并
// 在使用具体某个reducer的时候要注意命名空间
combineReducers({counter: counterReducer}),
applyMiddleware(logger, thunk)
);
-
关于
redux
的原理可以了解下,使用redux
的时候通常先通过调用createStore
方法初始化一个store
,如下:
const store = createStore(
// combineReducers将多个模块的reducer合并
// 在使用具体某个reducer的时候要注意命名空间
combineReducers({user}),
applyMiddleware(logger, thunk)
);
-
createStore
接收2个参数,一个是reducer
,当有多个reducer
的时候,使用combineReducers
,另一个我们叫enhancer
,可以理解为强化器 - 在上面的代码使用中,这里的
enhancer
就是applyMiddleware
中间件函数,enhancer
存在的时候会首先用于强化我们的store
export function createStore(reducer, enhancer) {
if(enhancer) {
return enhancer(createStore)(reducer)
}
}
-
store
的基本api
分别是:getState
、subscribe
、dispatch
,getState
的原理其实就是return currenState
返回当前状态值,subscribe
主要用于组件对store
的监听,dispatch
用于执行reducer
,根据action
的具体指令执行对应的方法对状态进行更新,然后返回全新的状态,执行完毕后通知所有监听器执行更新
export function createStore(reducer, enhancer) {
if(enhancer) {
return enhancer(createStore)(reducer)
}
// 当前状态值、监听器(监听store变化并执行的回调函数)
let currentState = []
let currentListeners = []
function getState () {
return currentState
}
function subscribe (listener) {
currentListeners.push(listener)
}
function dispatch (action) {
currentState = reducer(currentState, action)
currentListeners.forEach(...)
return action
}
return {getState, subscribe, dispatch}
}
-
applyMiddleware
接收若干个中间件,并且是按顺序接收的,然后中间件是希望放在Action
、reducer
之前执行的,通过一些方法将若干个中间件组成一个中间件链
export function applyMiddleware (...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = store.dispatch
// 当前基本api都先赋值给中间件
const midApi = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
// 组成中间件链
const middlewareChain = middlewares.map(middleware => middleware(midApi))
// compose方法可以将以方法组成的数组变成函数层层嵌套,也就是函数复合
diapatch = compose(...middlewareChain)(store.dispatch)
return {
...store,
dispatch
}
}
}
-
react-redux
的原理主要涉及到新增的2个api
,分别是connect
、provider
-
connect
主要用于将state
、action
相关内容绑定至组件,原理上主要是使用了上下文context
将state
进行隔代传递 -
provider
做的主要就是上下文提供,将store
传给需要的组件