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 cols 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.