react中多个页面,数据相互依赖reducer问题及解决

 更新时间:2025年12月20日 15:52:02   作者:初遇你时动了情  
文章介绍了一个电商商品管理页面的复杂状态管理场景,使用useReducer或useImmerReducer来集中管理状态,避免组件间直接监听,简化联动逻辑,防止死循环,并便于维护和扩展

场景

典型的电商商品管理页面复杂状态场景,涉及多个模块数据联动、动态生成 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} />
      <PlatformCategorySelector
        selected={state.basicInfo.platformCategoryId}
        onChange={setPlatformCategory}
      />
    </div>
  );
};

  • 各组件只关注自己的 slice 数据
  • 不用管其他模块数据的联动
  • 所有复杂联动逻辑在 hook/reducer 内集中处理

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • webpack4+react多页面架构的实现

    webpack4+react多页面架构的实现

    webpack在单页面打包上应用广泛,以create-react-app为首的脚手架众多。这篇文章主要介绍了webpack4+react多页面架构的实现,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-10-10
  • ReactJS应用程序中设置Axios拦截器方法demo

    ReactJS应用程序中设置Axios拦截器方法demo

    这篇文章主要为大家介绍了ReactJS应用程序中设置Axios拦截器方法demo,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • React Antd Upload组件上传多个文件实现方式

    React Antd Upload组件上传多个文件实现方式

    为实现多文件上传,需使用beforeUpload和customRequest替代onChange以避免多次调用问题,并处理文件路径以兼容Electron不同平台
    2025-08-08
  • react-router6.x路由配置及导航详解

    react-router6.x路由配置及导航详解

    这篇文章主要介绍了react-router6.x路由配置及导航,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-10-10
  • React中refs的一些常见用法汇总

    React中refs的一些常见用法汇总

    Refs是一个 获取 DOM节点或React元素实例的工具,在React中Refs 提供了一种方式,允许用户访问DOM 节点或者在render方法中创建的React元素,这篇文章主要给大家介绍了关于React中refs的一些常见用法,需要的朋友可以参考下
    2021-07-07
  • React封装高阶组件实现路由权限的控制详解

    React封装高阶组件实现路由权限的控制详解

    这篇文章主要介绍了React封装高阶组件实现路由权限的控制,在React中,为了实现安全可靠的路由权限控制,可以通过多种方式来确保只有经过授权的用户才能访问特定路径下的资源,下面来介绍封装高阶组件控制的方法,需要的朋友可以参考下
    2025-02-02
  • 详解React Angular Vue三大前端技术

    详解React Angular Vue三大前端技术

    当前世界中,技术发展非常迅速并且变化迅速,开发者需要更多的开发工具来解决不同的问题。本文就对于当下主流的前端开发技术React、Vue、Angular这三个框架做个相对详尽的探究,目的是为了解开这些前端技术的面纱,看看各自的庐山真面目。
    2021-05-05
  • React immer与Redux Toolkit使用教程详解

    React immer与Redux Toolkit使用教程详解

    这篇文章主要介绍了React中immer与Redux Toolkit的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-10-10
  • 详解react组件通讯方式(多种)

    详解react组件通讯方式(多种)

    这篇文章主要介绍了详解react组件通讯方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • React Hooks核心原理深入分析讲解

    React Hooks核心原理深入分析讲解

    这篇文章主要介绍了react hooks实现原理,文中给大家介绍了useState dispatch 函数如何与其使用的 Function Component 进行绑定,节后实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2022-12-12

最新评论