当前位置: 首页 > ds >正文

react中多个页面,数据相互依赖reducer解决方案

场景

典型的电商商品管理页面复杂状态场景,涉及多个模块数据联动、动态生成 SKU、属性和额外参数注入等。关键点是 避免组件之间直接相互监听,保持逻辑集中化

页面模块:

  • 基本信息
  • 商品名称
  • 分类 / 平台分类
  • 详细信息(富文本/描述)
  • 规格名称、规格值
  • SKU 列表:基于规格动态生成
  • SKU 可能包含额外字段(如价格、库存、额外参数)
  • 动态数据依赖

切换平台分类 → 获取动态属性、额外参数 → 注入到 SKU

修改规格名称 → 重新生成 SKU

  • 核心问题:

    • SKU 生成依赖规格数据、额外参数
  • 额外参数依赖平台分类

  • 数据变化涉及多个模块联动,容易出现重复计算或死循环

useReducer(或者 useImmerReducer) 比直接在组件里用一堆 useState + useEffect 更适合,原因如下:

  • 集中管理状态:所有页面状态都在一个地方管理,组件只负责渲染和触发操作。
  • 统一处理联动逻辑:SKU 生成、额外参数注入、动态属性拉取都在 reducer 内完成,不会散落在各组件的 useEffect 里。
  • 避免死循环:不需要组件间相互监听状态变化,更新逻辑集中在 reducer 内部。
  • 便于维护和扩展:新增字段或联动规则,只需改 reducer,不用改每个组件

状态设计

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>; // 平台分类动态属性extraParams: Record<string, any>;       // 平台分类额外参数skuList: SKU[];
}interface SKU {id?: string;specCombination: string[]; // 每个SKU对应的规格值组合price: number;stock: number;extraParams?: Record<string, any>;
}const initialState: ProductState = {basicInfo: {name: '',categoryId: 0,platformCategoryId: 0,description: ''},specs: [],dynamicAttributes: {},extraParams: {},skuList: []
};

Reducer 设计(包含联动逻辑)

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[] {// 简化示例:生成规格组合的笛卡尔积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 联动逻辑和额外参数注入都在 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} /><PlatformCategorySelectorselected={state.basicInfo.platformCategoryId}onChange={setPlatformCategory}/></div>);
};
  • 各组件只关注自己的 slice 数据

  • 不用管其他模块数据的联动

  • 所有复杂联动逻辑在 hook/reducer 内集中处理

在这里插入图片描述

http://www.xdnf.cn/news/18750.html

相关文章:

  • 文生3D实战:用[灵龙AI API]玩转AI 3D模型 – 第7篇
  • 青少年机器人技术(四级)等级考试试卷-实操题(2021年12月)
  • Boost.Asio 库中的 async_read_some用法
  • 我从零开始学习C语言(14)- 基本类型 PART1
  • vscode 中自己使用的 launch.json 设置
  • 5.7 生成环境使用cdn加载element ui
  • ASCOMP PDF Conversa:高效精准的PDF转换工具
  • pcie实现虚拟串口
  • 人工智能之数学基础:离散随机变量和连续随机变量
  • Java中如何实现对象的拷贝
  • RHCSA--命令(一)
  • 我是如何写作的?
  • Manus AI 与多语言手写识别技术文章大纲
  • 单例模式与线程池
  • 【Vue✨】Vue 中的 diff 算法详解
  • 云原生概述
  • git的工作使用中实际经验
  • 【码蹄杯】2025年本科组省赛第一场
  • 【Linux系统】命名管道与共享内存
  • 硬件笔记(27)---- 恒流源电路原理
  • [Redis进阶]---------持久化
  • 如何查看MySQL 的执行计划?
  • Spring Boot 3为何强制要求Java 17?
  • JavaScript 性能优化实战技术文章大纲
  • Games 101 第四讲 Transformation Cont(视图变换和投影变换)
  • 深入剖析结构体内存对齐
  • 边缘计算服务器EMI滤波器 故障分析与解决思路
  • 【LeetCode 热题 100】300. 最长递增子序列——(解法一)记忆化搜索
  • C++ 20: Concepts 与Requires
  • 链表-23.合并K个升序链表-力扣(LeetCode)