Redux是一个常用的跨组件状态管理库,大家都很熟悉。那么,如果让我们来实现一个类似的redux,应该如何处理呢?
需求分析
Target:实现一个简易的redux redux.js.org/introductio...
从文档中我们了解到,Redux 的核心是 reducer。reducer 中包含 state 和 action 来控制状态的变化。
- reducer 纯函数(状态变化的规则)
- subscribe/dispatch (订阅发布)
- getState(获取当前状态)
我们以计数器为例,写下如下reducer
(状态改变规则)
tsx
function countReducer(state = 0, action) {
switch (action.type) {
case 'ADD':
return state + 1
case 'MINUS':
return state - 1
default:
return state
}
}
简易实现
我们可以先看看源码是怎么实现的
github.com/reduxjs/red...
HappyPath
tsx
// createStore
export default function createStore(reducer) {
// 存储状态
let currentState
// 存储监听
let currentListeners = []
// 获取当前的state
function getState() {
return currentState
}
// 订阅
function subscribe(listener) {
currentListeners.push(listener)
// 取消订阅
return () => {
const index = currentListeners.indexOf(listener)
currentListeners.splice(index, 1)
}
}
// 派发action
function dispatch(action) {
currentState = reducer(currentState, action)
currentListeners.forEach((listener) => listener())
}
// 初始化
dispatch({ type: '@@REDUX_INIT' })
return {
getState,
subscribe,
dispatch
}
}
页面中使用
tsx
// Redux page
import React, { Component } from 'react'
import store from '../store/'
export default class ReduxPage extends Component {
componentDidMount() {
this.unsubscribe = store.subscribe(() => {
this.forceUpdate()
})
}
componentWillUnmount() {
this.unsubscribe()
}
add = () => {
store.dispatch({ type: 'ADD' })
}
minus = () => {
store.dispatch({ type: 'MINUS' })
}
render() {
return (
<div>
<h3>ReduxPage</h3>
<p>{store.getState()}</p>
<button onClick={this.add}>ADD</button>
<button onClick={this.minus}>minus</button>
</div>
)
}
}
到目前为止,我们已经成功实现了Redux的核心功能------数据订阅和发布管理。
applyMiddleware实现
在使用过程中,我们可能需要在状态变化过程中进行拦截处理。这时候就需要用到中间件。 中间件是一个函数,在Redux的dispatch过程中可以拦截和处理action。通过使用中间件,可以在将action发送给reducer之前对其进行额外处理,例如记录日志、执行异步操作、跳转路由等。那么,怎么实现呢?
需求:
- 有一个注册函数来注入中间件
- 每次调用dispatch时,执行中间件
我们可以参考Koa
中间件的原理,利用洋葱圈模型来依次执行中间件,可以写出如下代码:
tsx
import compose from './compose'
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer) => {
const store = createStore(reducer)
let dispatch = store.dispatch
const midApi = {
getState: store.getState,
// 保证是最新的dispatch
dispatch: (action, ...args) => dispatch(action, ...args)
}
const middlewareChain = middlewares.map((middleware) => middleware(midApi))
// 加强的dispatch
// 所有中间件的集合,同时执行store.dispatch
dispatch = compose(...middlewareChain)(store.dispatch)
return {
...store,
// 加强的dispatch
dispatch
}
}
}
tsx
// compose
export default function compose(...funcs) {
if (funcs.length === 0) {
return (arg) => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce(
(a, b) =>
(...args) =>
a(b(...args))
)
}
这时候我们需要改造下createStore
tsx
// createStore
export default function createStore(
reducer,
// 增强
enhancer
) {
if (enhancer) {
return enhancer(createStore)(reducer)
}
// 省略代码....
}
我们来测试一下,引入redux-logger
成功打印出日志,可以看出,我们已经成功实现了applyMiddle功能。
combineReducers实现
在开发过程中,可能会有多个状态(state),但需要对这些状态进行管理。这时候就可以使用combineReducer来将多个状态组合成一个整体的状态。
tsx
export default function combineReducers(reducers) {
// 返回一个合并后的reducer函数
return function combination(state = {}, action) {
// 生成新的state
let nextState = {}
// 标识state是否发生变化
let hasChanged = false
for (const key in reducers) {
const reducer = reducers[key]
// 之前key的state
const previousStateForKey = state[key]
// 执行reducer,获得新的state
const nextStateForKey = reducer(previousStateForKey, action)
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
nextState[key] = nextStateForKey
}
hasChanged =
hasChanged || Object.keys(nextState).length !== Object.keys(state).length
return hasChanged ? nextState : state
}
}
执行代码,可以看到当前的state
已经变成了对象,而countReducer
以key-value
的形式存储
Plugin
redux-logger 实现
- 打印日志
tsx
function logger({ getState, dispatch }) {
return (next) => (action) => {
console.log(`--------------`)
console.log(`action type ${action.type}`)
console.log(`prev state`, getState())
const returnValue = next(action)
console.log(`next state`, getState())
console.log(`--------------`)
return returnValue
}
}
redux-thunk 实现
- 允许
action
传入函数
tsx
function thunk({ getState, dispatch }) {
return (next) => (action) => {
if (typeof action === 'function') {
return action(dispatch, getState)
}
return next(action)
}
}
redux-promise 实现
- 支持
action
异步
tsx
function promise({ getState, dispatch }) {
return (next) => (action) => {
return isPromise(action) ? action.then(dispatch) : next(action)
}
}
总结
通过上述代码实现,我们了解到redux是通过一个独立的存储库来存储状态,并使用发布-订阅模式来更新状态。同时,引入了洋葱模型以注册中间件,用于处理异步操作和其他副作用。
在Plugin
部分,我们也简单的实现了部分中间件的逻辑,通过这些实现,我们可以更深入的了解到redux
中间件的作用以及原理。