使用扩展运算符推入具有嵌套级别的对象数组

Push into array of objects with nested level using spread operator

我有一个包含以下格式的对象数组的对象,该对象存储在一个状态中

const [value, setVal] = useState({ available : [], done: [] })

done 是具有以下结构的对象数组

[{col: "test", val: ["abc","xyz"]}]

我正在编写一个将 fieldvalue 作为输入参数的函数。如果该字段存在于 done 数组中,那么我需要将它推入其对应的 val 数组中。否则我需要创建一个新对象,然后将其推入其中。我怎样才能做到这一点?

如何在这两种情况下设置状态?我试过的代码如下

function update(field, value){
    const filterIndex = value.done.findIndex((obj) => field === obj.col);
    if (filterIndex > -1) {
      value.done[filterIndex].val.push(value);
    } else {
      setVal({
        ...value,
        done: [
          ...value.done,
          {
            col: field,
            val: [value],
          }
        ]
      });
    }
} 

这可能是一种可能的解决方案:

代码段

const stateVal = { available : [], done: [{col: "test", val: ["abc","xyz"]}] };

const updateStateVal = (sv, field, value) => ({
  ...sv,
  done: (
    sv.done.map(ob => ob.col).includes(field)
    ? sv.done.map(ob => (
      ob.col === field
      ? {...ob, val: ob.val.concat([value])}
      : {...ob}
    ))
    : sv.done.concat([{ col: field, val: [value] }])
  )
});

console.log('existing col test: ', updateStateVal(stateVal, 'test', 'def'));
console.log('non-exist col test2: ', updateStateVal(stateVal, 'test2', 'def'));

使用方法

setVal(prev => ({...updateStateVal(prev, field, value)}));

说明

  • objective是在donefield的presence/absence的基础上插入value

  • 方法updateStateVal需要3个参数,prev-statesvfieldvalue

  • 使用...展开运算符列出prev的所有属性(即sv)as-is

  • 现在,覆盖 done 属性

  • 首先通过与每个 array-element 中的 col 匹配来检查 prev-state 的 done 是否已经有 field .

  • 这是使用 .map() 结合 .includes()

    完成的
  • 如果找到,遍历 done 数组。

    对于 col 匹配 field 的数组元素,.concat 现有 val 数组与 value。所有其他元素变为 as-is.

  • 如果没有找到,只需.concat一个single-element数组([{ col: field, val: [value]}])到现有的done数组。

您应该为状态使用单独的变量名称而不是 value,因为该函数有一个局部变量也称为 value

像下面这样尝试。

const [values, setVals] = useState({ available: [], done: [] })

function update(field, value) {
  const filterIndex = values.done.findIndex((obj) => field === obj.col);
  if (filterIndex > -1) {
    // get a copy of the previous state done values
    const updatedDone = [...values.done];
    // update the new value in the copy of the array
    updatedDone[filterIndex].val.push(value);
    // update the state
    setVals((prevValue) => ({ ...prevValue, done: updatedDone }));
  } else {
    // add the new entry for the new filed with the value
    setVals((prevValue) => ({
      ...prevValue,
      done: [
        ...prevValue.done,
        {
          col: field,
          val: [value],
        },
      ],
    }));
  }
}