You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中如何更新嵌套状态里的数组中的数组?

解决React useReducer中嵌套数组状态的更新问题

问题分析

  1. REMOVE ITEM崩溃原因:你在REMOVE ITEM中错误地用对象展开语法{ ... }包裹了map返回的数组,导致返回的newState变成对象而非数组,后续调用layers.map自然触发类型错误。另外,data是数组类型,你用{ ...layer.data.filter(...) }把数组转成了对象,会破坏data的结构。
  2. UPDATE ITEM缺失实现:需要定位目标Layer和Item,通过不可变更新替换目标Item。
  3. 现有ADD逻辑的隐患:ADD LAYER和ADD ITEM直接修改原state数组/对象(push操作),违反React状态不可变原则,可能导致组件无法正确重新渲染。

修复与实现代码

1. 修复REMOVE ITEM

去掉对象包裹,直接返回map生成的新数组,同时正确处理data数组:

case "REMOVE ITEM": {
  return state.map(layer =>
    layer.name === action.layer.name
      ? { ...layer, data: layer.data.filter(({ id }) => id !== action.item.id) }
      : layer
  );
}

2. 实现UPDATE ITEM

定位目标Layer,遍历其data数组替换目标Item,全程保持不可变:

case "UPDATE ITEM": {
  return state.map(layer => {
    if (layer.name !== action.layer.name) return layer;
    const updatedData = layer.data.map(item =>
      item.id === action.item.id ? { ...item, ...action.item } : item
    );
    return { ...layer, data: updatedData };
  });
}

3. 修正ADD LAYER和ADD ITEM的不可变更新

虽然这两个功能当前可用,但为避免后续渲染问题,改为符合不可变原则的写法:

case "ADD LAYER": {
  const layerExists = state.some(l => l.name === action.layer.name);
  if (!layerExists) {
    return [...state, action.layer];
  }
  return state;
}

case "ADD ITEM": {
  return state.map(layer => {
    if (layer.name !== action.layer.name) return layer;
    const itemExists = layer.data.some(item => item.id === action.item.id);
    if (!itemExists) {
      return { ...layer, data: [...layer.data, action.item] };
    }
    return layer;
  });
}

完整修正后的useReducer代码

type ActionType =
  | { type: "ADD LAYER"; layer: Layer }
  | { type: "ADD ITEM"; item: Item; layer: Layer }
  | { type: "UPDATE ITEM"; item: Item; layer: Layer }
  | { type: "REMOVE ITEM"; item: Item; layer: Layer };

const [layers, dispatch] = useReducer((state: Layer[], action: ActionType) => {
  switch (action.type) {
    case "ADD LAYER": {
      const layerExists = state.some(l => l.name === action.layer.name);
      if (!layerExists) {
        return [...state, action.layer];
      }
      return state;
    }
    case "ADD ITEM": {
      return state.map(layer => {
        if (layer.name !== action.layer.name) return layer;
        const itemExists = layer.data.some(item => item.id === action.item.id);
        if (!itemExists) {
          return { ...layer, data: [...layer.data, action.item] };
        }
        return layer;
      });
    }
    case "UPDATE ITEM": {
      return state.map(layer => {
        if (layer.name !== action.layer.name) return layer;
        const updatedData = layer.data.map(item =>
          item.id === action.item.id ? { ...item, ...action.item } : item
        );
        return { ...layer, data: updatedData };
      });
    }
    case "REMOVE ITEM": {
      return state.map(layer =>
        layer.name === action.layer.name
          ? { ...layer, data: layer.data.filter(({ id }) => id !== action.item.id) }
          : layer
      );
    }
    default:
      throw new Error();
  }
}, initialLayers);

关键要点总结

  • 不可变更新核心:永远不直接修改原state对象/数组,通过展开语法(...)创建新对象/数组,确保React能检测到状态变化。
  • 嵌套数组处理:多层嵌套数组需逐层浅拷贝,仅修改目标层级内容,其他层级保持引用不变。
  • 类型区分:不要混淆数组与对象的展开语法,数组展开后仍为数组,对象展开后为对象,误用会触发类型错误。

内容的提问来源于stack exchange,提问作者anton

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 21:18:18