Redux 原理深度剖析
1. Redux 实现
定义 Action 和 Reducer 类型,为了简便,先用JavaScript来演示。
1.1. 定义Action和Reducer类型
// 定义 Action 类型
/*** @typedef {Object} Action* @property {string} type*/// 定义 Reducer 类型
/*** @callback Reducer* @param {any} state* @param {Action} action* @returns {any}*/
Action 对象包含一个 type 属性,用于描述要执行的操作。
Reducer 是一个函数,接收当前状态和 Action,并返回新的状态。
1.2. 创建Store
// 创建 store
/*** @type {CreateStore}*/
function createStore(reducer, initialState, enhancer) {if (enhancer) {return enhancer(createStore)(reducer, initialState);}let state = initialState;let listeners = [];function getState() {return state;}function dispatch(action) {state = reducer(state, action);listeners.forEach(listener => listener());}function subscribe(listener) {listeners.push(listener);return () => {listeners = listeners.filter(l => l !== listener);};}return { getState, dispatch, subscribe };
}
createStore 函数用于创建 Redux store。
getState 方法返回当前状态。
dispatch 方法接收一个 Action,并使用 reducer 计算新状态。
subscribe 方法用于订阅状态变化。
1.3. 合并多个 Reducer
// 合并多个 reducer
/*** @param {Object<string, Reducer>} reducers* @returns {Reducer}*/
function combineReducers(reducers) {return (state = {}, action) => {const newState = {};for (const key in reducers) {newState[key] = reducers[key](state[key], action);}return newState;};
}
combineReducers 函数将多个 reducer 合并成一个,以便管理复杂的状态结构。
1.4. 组合函数
// 组合函数
/*** @param {...function} funcs* @returns {function}*/
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)));
}
compose 函数用于组合多个函数,从右到左依次执行。
1.5. 应用中间件
// 应用中间件
/*** @param {...Middleware} middlewares* @returns {function(CreateStore): CreateStore}*/
function applyMiddleware(...middlewares) {return createStore => (reducer, initialState) => {const store = createStore(reducer, initialState);let dispatch = store.dispatch;const middlewareAPI = {getState: store.getState,dispatch: action => dispatch(action),};const chain = middlewares.map(middleware => middleware(middlewareAPI));dispatch = compose(...chain)(store.dispatch);return {...store,dispatch,};};
}
applyMiddleware 函数用于应用中间件,增强 dispatch 方法。
1.6. Redux整体源码实现
// 定义 Action 类型
/*** @typedef {Object} Action* @property {string} type*/// 定义 Reducer 类型
/*** @callback Reducer* @param {any} state* @param {Action} action* @returns {any}*/// 定义 Store 类型
/*** @typedef {Object} Store* @property {function(): any} getState* @property {function(Action): void} dispatch* @property {function(function(): void): function(): void} subscribe*/// 创建 store
/*** @type {CreateStore}*/
function createStore(reducer, initialState, enhancer) {if (enhancer) {return enhancer(createStore)(reducer, initialState);}let state = initialState;let listeners = [];function getState() {return state;}function dispatch(action) {state = reducer(state, action);listeners.forEach(listener => listener());}function subscribe(listener) {listeners.push(listener);return () => {listeners = listeners.filter(l => l !== listener);};}return { getState, dispatch, subscribe };
}// 合并多个 reducer
/*** @param {Object<string, Reducer>} reducers* @returns {Reducer}*/
function combineReducers(reducers) {return (state = {}, action) => {const newState = {};for (const key in reducers) {newState[key] = reducers[key](state[key], action);}return newState;};
}// 组合函数
/*** @param {...function} funcs* @returns {function}*/
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)));
}// 应用中间件
/*** @param {...Middleware} middlewares* @returns {function(CreateStore): CreateStore}*/
function applyMiddleware(...middlewares) {return createStore => (reducer, initialState) => {const store = createStore(reducer, initialState);let dispatch = store.dispatch;const middlewareAPI = {getState: store.getState,dispatch: action => dispatch(action),};const chain = middlewares.map(middleware => middleware(middlewareAPI));dispatch = compose(...chain)(store.dispatch);return {...store,dispatch,};};
}// 示例代码
const initialState = { count: 0 };const counterReducer = (state = initialState, action) => {switch (action.type) {case 'INCREMENT':return { count: state.count + 1 };case 'DECREMENT':return { count: state.count - 1 };default:return state;}
};const loggerMiddleware = ({ getState }) => next => action => {console.log('will dispatch', action);next(action);console.log('state after dispatch', getState());
};const store = createStore(counterReducer,initialState,applyMiddleware(loggerMiddleware)
);store.subscribe(() => {console.log('state updated:', store.getState());
});store.dispatch({ type: 'INCREMENT' });
store.dispatch({ type: 'DECREMENT' });
2. 示例代码
2.1. 定义 Reducer 和初始状态
const initialState = { count: 0 };const counterReducer = (state = initialState, action) => {switch (action.type) {case 'INCREMENT':return { count: state.count + 1 };case 'DECREMENT':return { count: state.count - 1 };default:return state;}
};
counterReducer 是一个简单的 reducer,处理 INCREMENT 和 DECREMENT 两种 action。
2.2. 定义中间件
const loggerMiddleware = ({ getState }) => next => action => {console.log('will dispatch', action);next(action);console.log('state after dispatch', getState());
};
loggerMiddleware 是一个日志中间件,用于在 action 分发前后打印日志。
2.3. 创建 Store 并应用中间件
const store = createStore(counterReducer,initialState,applyMiddleware(loggerMiddleware)
);store.subscribe(() => {console.log('State updated:', store.getState());
});store.dispatch({ type: 'INCREMENT' });
store.dispatch({ type: 'DECREMENT' });
创建 store 并应用 loggerMiddleware 中间件。
订阅状态变化并分发两个 action。
3. 将 Redux 与 React 结合
3.1. 创建自定义 Hook
import React from 'react';
import { useSyncExternalStore } from 'react';/*** 自定义 Hook,使用 useSyncExternalStore 订阅 Redux store 的状态变化* @param {Store} store* @returns {any} 当前状态*/
function useReduxStore(store) {return useSyncExternalStore(store.subscribe, // 订阅状态变化store.getState, // 获取当前状态store.getState // SSR 期间获取当前状态 (此处简化处理));
}
useReduxStore 是一个自定义 Hook,利用 useSyncExternalStore 订阅 Redux store 的状态变化。
3.2. 编写示例组件
function Counter() {const state = useReduxStore(store); // 使用自定义 Hook 获取 Redux 状态return (<div><p>Count: {state.count}</p><button onClick={() => store.dispatch({ type: 'INCREMENT' })}>Increment</button><button onClick={() => store.dispatch({ type: 'DECREMENT' })}>Decrement</button></div>);
}
Counter 组件使用 useReduxStore Hook 获取当前状态,并通过 Redux store 分发动作。
3.3. 渲染组件
import { createRoot } from 'react-dom/client';const container = document.getElementById('root');
const root = createRoot(container);
root.render(<Counter />);
// 将 Counter 组件渲染到页面
使用 createRoot 渲染 Counter 组件到页面上的 DOM 节点中。
4. Mantine 的状态 ts 实现
import { useSyncExternalStore } from 'react';export type MantineStoreSubscriber<Value> = (value: Value) => void;
type SetStateCallbackValue = (value: Value) => Value;export interface MantineStore<Value> {getState: () => Value;setState: (value: Value | SetStateCallbackValue) => void;updateState: (value: Value | SetStateCallbackValue) => void;initialize: (value: Value) => void;subscribe: (callback: MantineStoreSubscriber<Value>) => () => void;
}export type MantineStoreValue<Store extends MantineStore<any>> = ReturnType<Store['getState']>;export function createStore<Value extends Record<string, any>>(initialState: Value
): MantineStore<Value> {let state = initialState;let initialized = false;const listeners = new Set<MantineStoreSubscriber<Value>>();return {getState() {return state;},updateState(value) {state = typeof value === 'function' ? value(state) : value;},setState(value) {this.updateState(value);listeners.forEach((listener) => listener(state));},initialize(value) {if (!initialized) {state = value;initialized = true;}},subscribe(callback) {listeners.add(callback);return () => listeners.delete(callback);},};
}export function useStore<Store extends MantineStore<any>>(store: Store) {return useSyncExternalStore<MantineStoreValue<Store>>(store.subscribe,() => store.getState(),() => store.getState());
}
5. Redux 的其他概念
异步的支持,因为 reducer 的设计,导致处理过程是依照纯函数和同步函数处理的,所以我们需要额外考虑异步的事情,使用 redux-thunk、redux-sage的方案。