简体   繁体   中英

Push into array of objects with nested level using spread operator

I am having an object containing array of objects with following format which is stored in a state

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

done is an array of objects with following structure

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

I am writing a function that takes field and value as input parameters. If the field is present in done array, then I need to push it inside its corresponding val array. Else I need to create a new object and then push it inside. How can I achieve this?

How can I set the state in both the cases?Code that I tried is below

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],
          }
        ]
      });
    }
} 

You should use a separate variable name for the state instead of value since the function has a local variable also called value .

Try like below.

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],
        },
      ],
    }));
  }
}

This may be one possible solution:

Code Snippet

 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'));

How to use

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

Explanation

  • The objective is to insert value based on presence/absence of field in done .

  • The method updateStateVal takes 3 params, the prev-state sv , the field & the value

  • Use ... spread operator to list down all props of prev (ie, sv ) as-is

  • Now, override the done prop

  • First check if the prev-state's done already has field by matching with the col s from each array-element.

  • This is done using .map() in conjunction with .includes()

  • If found, iterate through the done array.

    For the array element where col matches field , .concat the existing val array with value . All other elements go as-is.

  • If not found, simply .concat a single-element array ( [{ col: field, val: [value]}] ) to existing done array.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM