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的方案。