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

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.dtcms.com/a/346724.html

相关文章:

  • 变频器实习DAY35
  • 深入理解Java多线程:状态、安全、同步与通信
  • Day12 数据统计-Excel报表
  • 基于llama.cpp的量化版reranker模型调用示例
  • 目标跟踪 YOLO11 单目标跟踪
  • Uipath查找元素 查找子元素 获取属性活动组合使用示例
  • 【数据结构】线性表——链表
  • 基于springboot购物商城系统源码
  • 灵动AI:工业级商品图AI生成工具
  • 【剖析高并发秒杀】从流量削峰到数据一致性的架构演进与实践
  • GaussDB 数据库架构师修炼(十八) SQL引擎-解析器
  • 慢查询该怎么优化
  • 【文献阅读】Lossless data compression by large models
  • 【卷积神经网络详解与实例】2——卷积计算详解
  • Hive中的join优化
  • 解决散点图绘制算法单一导致的数据异常问题
  • DeepSpeed v0.17.5发布:优化性能与扩展功能的全新升级
  • Axure:有个特别实用的功能
  • 寻找AI——高保真还原设计图生成App页面
  • 【K8s】整体认识K8s之Docker篇
  • 完整实验命令解析:从集群搭建到负载均衡配置
  • 在TencentOS3上部署OpenTenBase:从入门到实战的完整指南
  • week4-[循环结构]生日悖论-new
  • 【C语言16天强化训练】从基础入门到进阶:Day 8
  • 【基础-判断】Video组件可以支持本地视频路径和网络路径播放。播放网络视频时,需要申请权限ohos.permission.INTERNET
  • Clustering Enabled Wireless Channel Modeling Using Big Data Algorithms
  • 学习游戏制作记录(合并更多的技能与技能树)8.23
  • 祝贺,国产轻量级桌面GIS软件Snaplayers下载量突破上万
  • 【技术突破】动态目标误检率↓83.5%!陌讯多模态融合算法在智慧城管的实战优化
  • 算法训练营day60 图论⑩ Bellman_ford 队列优化算法、判断负权回路、单源有限最短路