react中多個頁面,數(shù)據相互依賴reducer問題及解決
更新時間:2025年12月20日 15:52:02 作者:初遇你時動了情
文章介紹了一個電商商品管理頁面的復雜狀態(tài)管理場景,使用useReducer或useImmerReducer來集中管理狀態(tài),避免組件間直接監(jiān)聽,簡化聯(lián)動邏輯,防止死循環(huán),并便于維護和擴展
場景
典型的電商商品管理頁面復雜狀態(tài)場景,涉及多個模塊數(shù)據聯(lián)動、動態(tài)生成 SKU、屬性和額外參數(shù)注入等。關鍵點是 避免組件之間直接相互監(jiān)聽,保持邏輯集中化
頁面模塊
- 基本信息
- 商品名稱
- 分類 / 平臺分類
- 詳細信息(富文本/描述)
- 規(guī)格名稱、規(guī)格值
- SKU 列表:基于規(guī)格動態(tài)生成
- SKU 可能包含額外字段(如價格、庫存、額外參數(shù))
- 動態(tài)數(shù)據依賴
切換平臺分類 → 獲取動態(tài)屬性、額外參數(shù) → 注入到 SKU
修改規(guī)格名稱 → 重新生成 SKU
核心問題:
- SKU 生成依賴規(guī)格數(shù)據、額外參數(shù)
- 額外參數(shù)依賴平臺分類
- 數(shù)據變化涉及多個模塊聯(lián)動,容易出現(xiàn)重復計算或死循環(huán)
useReducer(或者 useImmerReducer) 比直接在組件里用一堆 useState + useEffect 更適合,原因如下:
- 集中管理狀態(tài):所有頁面狀態(tài)都在一個地方管理,組件只負責渲染和觸發(fā)操作。
- 統(tǒng)一處理聯(lián)動邏輯:SKU 生成、額外參數(shù)注入、動態(tài)屬性拉取都在 reducer 內完成,不會散落在各組件的 useEffect 里。
- 避免死循環(huán):不需要組件間相互監(jiān)聽狀態(tài)變化,更新邏輯集中在 reducer 內部。
- 便于維護和擴展:新增字段或聯(lián)動規(guī)則,只需改 reducer,不用改每個組件
狀態(tài)設計
import { useReducer } from 'react';
import produce from 'immer';
interface ProductState {
basicInfo: {
name: string;
categoryId: number;
platformCategoryId: number;
description: string;
};
specs: {
name: string;
values: string[];
}[];
dynamicAttributes: Record<string, any>; // 平臺分類動態(tài)屬性
extraParams: Record<string, any>; // 平臺分類額外參數(shù)
skuList: SKU[];
}
interface SKU {
id?: string;
specCombination: string[]; // 每個SKU對應的規(guī)格值組合
price: number;
stock: number;
extraParams?: Record<string, any>;
}
const initialState: ProductState = {
basicInfo: {
name: '',
categoryId: 0,
platformCategoryId: 0,
description: ''
},
specs: [],
dynamicAttributes: {},
extraParams: {},
skuList: []
};
Reducer 設計(包含聯(lián)動邏輯)
type Action =
| { type: 'UPDATE_BASIC_INFO'; payload: Partial<ProductState['basicInfo']> }
| { type: 'UPDATE_SPEC'; payload: { index: number; spec: Partial<ProductState['specs'][0]> } }
| { type: 'SET_PLATFORM_CATEGORY'; payload: { platformCategoryId: number; dynamicAttributes: any; extraParams: any } }
| { type: 'UPDATE_SKU'; payload: SKU[] };
function generateSKUList(specs: ProductState['specs'], extraParams: ProductState['extraParams']): SKU[] {
// 簡化示例:生成規(guī)格組合的笛卡爾積
if (specs.length === 0) return [];
function cartesian(arrays: string[][]): string[][] {
return arrays.reduce<string[][]>(
(a, b) => a.flatMap(d => b.map(e => [...d, e])),
[[]]
);
}
const specValues = specs.map(s => s.values.length ? s.values : ['']);
const combinations = cartesian(specValues);
return combinations.map(comb => ({
specCombination: comb,
price: 0,
stock: 0,
extraParams: { ...extraParams }
}));
}
function productReducer(state: ProductState, action: Action): ProductState {
switch (action.type) {
case 'UPDATE_BASIC_INFO':
return { ...state, basicInfo: { ...state.basicInfo, ...action.payload } };
case 'UPDATE_SPEC':
return produce(state, draft => {
draft.specs[action.payload.index] = { ...draft.specs[action.payload.index], ...action.payload.spec };
draft.skuList = generateSKUList(draft.specs, draft.extraParams);
});
case 'SET_PLATFORM_CATEGORY':
return produce(state, draft => {
draft.basicInfo.platformCategoryId = action.payload.platformCategoryId;
draft.dynamicAttributes = action.payload.dynamicAttributes;
draft.extraParams = action.payload.extraParams;
draft.skuList = generateSKUList(draft.specs, draft.extraParams);
});
case 'UPDATE_SKU':
return { ...state, skuList: action.payload };
default:
return state;
}
}
自定義 Hook 封裝
export function useProductManager() {
const [state, dispatch] = useReducer(productReducer, initialState);
const updateBasicInfo = (payload: Partial<ProductState['basicInfo']>) => {
dispatch({ type: 'UPDATE_BASIC_INFO', payload });
};
const updateSpec = (index: number, spec: Partial<ProductState['specs'][0]>) => {
dispatch({ type: 'UPDATE_SPEC', payload: { index, spec } });
};
const setPlatformCategory = async (platformCategoryId: number) => {
const dynamicAttributes = await fetchDynamicAttributes(platformCategoryId);
const extraParams = await fetchExtraParams(platformCategoryId);
dispatch({
type: 'SET_PLATFORM_CATEGORY',
payload: { platformCategoryId, dynamicAttributes, extraParams }
});
};
const updateSKU = (skuList: SKU[]) => {
dispatch({ type: 'UPDATE_SKU', payload: skuList });
};
return { state, updateBasicInfo, updateSpec, setPlatformCategory, updateSKU };
}
- 組件只調用 updateXXX 或 setPlatformCategory
- SKU 聯(lián)動邏輯和額外參數(shù)注入都在 reducer 內完成
- 避免在組件里寫大量 useEffect
組件使用示例
const ProductPage = () => {
const { state, updateBasicInfo, updateSpec, setPlatformCategory, updateSKU } = useProductManager();
return (
<div>
<BasicInfoForm info={state.basicInfo} onChange={updateBasicInfo} />
<SpecsEditor specs={state.specs} onChange={updateSpec} />
<SKUList skuList={state.skuList} onChange={updateSKU} />
<PlatformCategorySelector
selected={state.basicInfo.platformCategoryId}
onChange={setPlatformCategory}
/>
</div>
);
};
- 各組件只關注自己的 slice 數(shù)據
- 不用管其他模塊數(shù)據的聯(lián)動
- 所有復雜聯(lián)動邏輯在 hook/reducer 內集中處理

總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
React Antd Upload組件上傳多個文件實現(xiàn)方式
為實現(xiàn)多文件上傳,需使用beforeUpload和customRequest替代onChange以避免多次調用問題,并處理文件路徑以兼容Electron不同平臺2025-08-08
React immer與Redux Toolkit使用教程詳解
這篇文章主要介紹了React中immer與Redux Toolkit的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧2022-10-10

