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 内集中处理

总结

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

相关文章

  • react之umi配置国际化语言locale的踩坑记录

    react之umi配置国际化语言locale的踩坑记录

    这篇文章主要介绍了react之umi配置国际化语言locale的踩坑记录,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • React中key属性的警告及解决方案

    React中key属性的警告及解决方案

    在使用 React 进行开发时,key 属性是一个至关重要的概念,尤其在渲染列表时,开发者在使用 key 属性时,常常会遇到各种警告信息,本文将详细解析这些警告的原因,提供有效的解决方案,并总结最佳实践,需要的朋友可以参考下
    2024-12-12
  • 详解React中常见的三种路由处理方式选择

    详解React中常见的三种路由处理方式选择

    这篇文章主要为大家详细介绍了React中常见的三种路由处理方式该如何选择,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-01-01
  • React拖拽调整大小的组件

    React拖拽调整大小的组件

    这篇文章主要为大家详细介绍了React拖拽调整大小的组件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • 详解基于React.js和Node.js的SSR实现方案

    详解基于React.js和Node.js的SSR实现方案

    这篇文章主要介绍了详解基于React.js和Node.js的SSR实现方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • React组件的使用详细讲解

    React组件的使用详细讲解

    React组件分为函数组件与class组件;函数组件是无状态组件,class称为类组件;函数组件只有props,没有自己的私有数据和生命周期函数;class组件有自己私有数据(this.state)和生命周期函数
    2022-11-11
  • React渲染机制及相关优化方案

    React渲染机制及相关优化方案

    这篇文章主要介绍了react中的渲染机制以及相关的优化方案,内容包括react渲染步骤、concurrent机制以及产生作用的机会,简单模拟实现 concurrent mode,基于作业调度优先级的思路进行项目优化的两个hooks,感兴趣的小伙伴跟着小编一起来看看吧
    2023-07-07
  • React Native中Mobx的使用方法详解

    React Native中Mobx的使用方法详解

    这篇文章主要给大家介绍了关于React Native中Mobx的使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-12-12
  • 一文详解React渲染优化之useImmer

    一文详解React渲染优化之useImmer

    在React日常开发中,我们常常被重复渲染或无意义渲染所折磨,穷尽脑汁,做各种优化:memo、useMemo、useCallback、immutable等,本文主要讲述immutable的简约版Immer,感兴趣的同学可以一起来学习
    2023-05-05
  • 前端使用 React Query 管理“服务器状态”的方法

    前端使用 React Query 管理“服务器状态”的方法

    文章介绍了如何安装和使用React Query来管理服务器状态,文章提供了一个实战项目示例,展示了如何使用React Query来实现获取、新增、删除和自动刷新Todo列表等功能,感兴趣的朋友跟随小编一起看看吧
    2025-11-11

最新评论