首页 > 解决方案 > 如何使用 useReducer 更新嵌套数组?

问题描述

我有这些:

export type DataItemChild = {
  id: number;
  title:string;
  checked?: boolean;
};

请注意,子项可以在此处未定义:

export type DataItems = {
  id: number;
  title:string;
  subItems?: Array<DataItemChild>;
  checked?: boolean;
};

export const initalState: StateType = {
  items: [],
  date: new Date(),
};

到目前为止,我已经弄清楚如何更新顶级对象上的数组,例如:

  case ActionKind.Checked:
    newArrayChecked = state.items.map((item) => ({
      ...item,
      checked: item.id === payload.id ? true : item.checked,
    }));
    return {
      ...state,
      items: newArrayChecked,
    };

我不知道如何更新子项?我有这个(精简)

export const reducer = (state: StateType, action: Action) => {
  const { type, payload } = action;
  let newArrayChildCheck: Array<DataItemChild> = []
  switch (type) {

    case ActionKind.UnCheckedSub:
      newArrayChildCheck = state.items.map((item) => ({
        ...item.subItems?.map((sub, index) => ({
          checked:  sub.id === payload.subItems?[index].id ? true: sub.checked
        })),
      }));
      return {
        ...state,
        items.subItems: newArrayChildCheck,
      };

    default:
      return state;
  }
};

但这给了我一个打字错误,说我的 newArrayChildCheck 不支持未定义。完整错误:

键入'{ [x:数字]:{检查:布尔| 不明确的; }; 长度?:数字 | 不明确的; toString?: (() => 字符串) | 不明确的; toLocaleString?: (() => 字符串) | 不明确的; 流行?:(()=> {检查:布尔|未定义;}|未定义)| 不明确的; ... 29 更多 ...; [Symbol.unscopables]?: (() => { ...; }) | 不明确的; }[]' 不可分配给类型 'DataItemChild[]'。键入'{ [x:数字]:{检查:布尔| 不明确的; }; 长度?:数字 | 不明确的; toString?: (() => 字符串) | 不明确的; toLocaleString?: (() => 字符串) | 不明确的; 流行音乐?:(()=

编辑:

在@Kelvin Schoofs 回答之后,我意识到我正在发送一个包含子项的整个对象。有效载荷是一种类型DataItems

我根据@Kelvin Schoofs 的建议修改了我的尝试,稍作改动:

  newArrayChildCheck = state.items.map((item) => ({
    ...item,
    subItems: item.subItems?.map((sub, index) => ({
        ...sub,
        checked: sub.id === payload.subItems[index].id ? true : sub.checked
    })),

不是[index]

但我越来越

对象可能未定义

标签: javascriptreactjstypescript

解决方案


似乎您错误地传播了新对象。您.map((item) => ...)返回一个对象,但您在其中传播了一个(潜在的)数组,该数组只会填充数字(好吧,字符串化版本)键。您可能打算从 和 复制原始字段,item并将映射结果分配给该字段。您在. 像这样的东西是类型正确的:idtitlesubItemssubItemssubItems?.map

newArrayChildCheck = state.items.map((item) => ({
    ...item,
    subItems: item.subItems?.map((sub) => ({
        ...sub,
        checked: sub.id === payload.id ? true : sub.checked
    })),
}));

那就是如果你想要你的DataItems数组的一个改变版本。如果你真的想返回每一个更改DataItemChild过的checked版本,你可以flatMap这样使用:

newArrayChildCheck = state.items.flatMap((item) => 
    item.subItems?.map((sub) => ({
        ...sub,
        checked: sub.id === payload.id ? true : sub.checked
    })),
);

推荐阅读