关注不迷路→Redux源码解析(2)
春暖花开,万物复苏,redux源码作者也在马不停蹄发5.0.0-alpha.2(截止2023.03),借着这个明媚的季节,让我们探索下redux的源码。
众所周知,redux集成许多状态管理库的优秀思想,“可预测数据变化”、“三大原则”等等众多概念令人眼花缭乱、望而却步,但是翻看源码其实非常短小精悍。
本系列分为两部分:
- 从createStore用法开始探讨state/action/dispatch/subscribe运转流程(本篇)
- 讨论applyMiddleware api到中间件运转流程(下一篇)
安装使用
通过CRA安装项目,并通过yarn add redux安装依赖,在App.tsx写入如下
import React, { useEffect, useRef } from "react";
import "./App.css";
import { type Store, createStore } from "redux";
interface Iaction {
type: string;
}
function App() {
const storeRef: React.MutableRefObject<Store | null> = useRef(null);
useEffect(() => {
const counterReducer = (state = { value: 0 }, action: Iaction) => {
switch (action.type) {
case "counter/incremented":
return { value: state.value + 1 };
case "counter/decremented":
return { value: state.value - 1 };
default:
return state;
}
};
storeRef.current = createStore(
counterReducer
);
storeRef.current.subscribe(() => {
console.log(storeRef.current?.getState());
});
});
return (
<div className="App">
<button
onClick={() =>
storeRef.current?.dispatch({ type: "counter/incremented" })
}
>
ADD
</button>
</div>
);
}
export default App;
页面效果如下:
以上代码包含redux的最基本要素(state、action、reducer),流程也很清晰:
- 声明一个结合state和action的reducer函数,传入createStore函数生成一个Store类型对象
- 通过Store类型对象的subscribe方法提供的回调函数进行事件监听
- Store类型对象dispatch,监听事件生效
源码解析
进入src/createStore.ts,进入主函数,可以看到三个入参reducer、 preloadedState、enhancer,enhancer与中间件有关先不细说。源码解析如下:
export function createStore<
S,
A extends Action,
Ext extends {} = {},
StateExt extends {} = {}
>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<S, A, StateExt> & Ext {
if (typeof reducer !== 'function') {
throw new Error(
`Expected the root reducer to be a function. Instead, received: '${kindOf(
reducer
)}'`
)
}
if (
(typeof preloadedState === 'function' && typeof enhancer === 'function') ||
(typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
throw new Error(
'It looks like you are passing several store enhancers to ' +
'createStore(). This is not supported. Instead, compose them ' +
'together to a single function. See https://redux.js.org/tutorials/fundamentals/part-4-store#creating-a-store-with-enhancers for an example.'
)
}
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState as StoreEnhancer<Ext, StateExt>
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error(
`Expected the enhancer to be a function. Instead, received: '${kindOf(
enhancer
)}'`
)
}
return enhancer(createStore)(
reducer,
preloadedState as PreloadedState<S>
) as Store<S, A, StateExt> & Ext
}
// 以上为防御性代码和进入中间件处理逻辑,略
// 当前reducer
let currentReducer = reducer
// 当前数据
let currentState = preloadedState as S
// 监听事件集合
let currentListeners: Map<number, ListenerCallback> | null = new Map()
// 监听事件副本
let nextListeners = currentListeners
// 监听事件id
let listenerIdCounter = 0
// 是否正在执行state的修改,作为一个锁,防止多个dispatch一起工作
let isDispatching = false
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = new Map()
currentListeners.forEach((listener, key) => {
nextListeners.set(key, listener)
})
}
}
// 获取总的数据
function getState(): S {
if (isDispatching) {
throw new Error(
'You may not call store.getState() while the reducer is executing. ' +
'The reducer has already received the state as an argument. ' +
'Pass it down from the top reducer instead of reading it from the store.'
)
}
return currentState as S
}
// 订阅事件
function subscribe(listener: () => void) {
if (typeof listener !== 'function') {
throw new Error(
`Expected the listener to be a function. Instead, received: '${kindOf(
listener
)}'`
)
}
// 检测是否并发
if (isDispatching) {
throw new Error(
'You may not call store.subscribe() while the reducer is executing. ' +
'If you would like to be notified after the store has been updated, subscribe from a ' +
'component and invoke store.getState() in the callback to access the latest state. ' +
'See https://redux.js.org/api/store#subscribelistener for more details.'
)
}
// 防止多次触发取消订阅
let isSubscribed = true
ensureCanMutateNextListeners()
const listenerId = listenerIdCounter++
// 并入事件集合
nextListeners.set(listenerId, listener)
return function unsubscribe() {
if (!isSubscribed) {
return
}
if (isDispatching) {
throw new Error(
'You may not unsubscribe from a store listener while the reducer is executing. ' +
'See https://redux.js.org/api/store#subscribelistener for more details.'
)
}
isSubscribed = false
ensureCanMutateNextListeners()
nextListeners.delete(listenerId)
currentListeners = null
}
}
function dispatch(action: A) {
if (!isPlainObject(action)) {
throw new Error(
`Actions must be plain objects. Instead, the actual type was: '${kindOf(
action
)}'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples.`
)
}
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. You may have misspelled an action type string constant.'
)
}
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
// 对状态的修改
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
// 执行所有监听事件
const listeners = (currentListeners = nextListeners)
listeners.forEach(listener => {
listener()
})
// 中间件执行需要透传的value
return action
}
function replaceReducer(nextReducer: Reducer<S, A>): void {
if (typeof nextReducer !== 'function') {
throw new Error(
`Expected the nextReducer to be a function. Instead, received: '${kindOf(
nextReducer
)}`
)
}
// 替换reducer
currentReducer = nextReducer
// 预埋事件
dispatch({ type: ActionTypes.REPLACE } as A)
}
// 适配rxjs之类的库的操作
function observable() {
const outerSubscribe = subscribe
return {
subscribe(observer: unknown) {
if (typeof observer !== 'object' || observer === null) {
throw new TypeError(
`Expected the observer to be an object. Instead, received: '${kindOf(
observer
)}'`
)
}
function observeState() {
const observerAsObserver = observer as Observer<S>
if (observerAsObserver.next) {
observerAsObserver.next(getState())
}
}
observeState()
const unsubscribe = outerSubscribe(observeState)
return { unsubscribe }
},
[$$observable]() {
return this
}
}
}
// When a store is created, an "INIT" action is dispatched so that every
// reducer returns their initial state. This effectively populates
// the initial state tree.
dispatch({ type: ActionTypes.INIT } as A)
const store = {
dispatch: dispatch as Dispatch<A>,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
} as unknown as Store<S, A, StateExt> & Ext
return store
}
总结以上的代码流程:
-
预先存储数据(state)和对数据的分类处理方法(reducer)
-
执行subscribe存储(或多次)订阅事件
-
dispatch函数触发reducer,修改state,执行存储的订阅事件,无入参