What is the best and clean way to alter Object Arrays?
I have a code that look´s like this.
const [get_post, set_post] = useState([
{name: "First"},
{name: "Second"},
{name: "Third"},
])
I would like to add and edit keys, on a certain index. So I do it like this:
<button onClick={()=>
set_post([...get_post, get_post[0].content = "This is index zero" ])
}> Manipulate! </button>
My result is this:
[
{"name": "First", "content": "This is index zero"},
{"name": "Second"},
{"name": "Third"},
"This is index zero" <-- This line is the problem
]
I have googled this a lot and this seems to be a common subject, however.
This post describe the same problem and solution with a keyed object, which doesn't help me.
React Hooks useState() with Object
This post support 3rd party libs and/or deep copying, which I suspect isn't the "right" way of doing it either.
Whats the best way to update an object in an array in ReactJS?
This thread also support a lot of deep copys and maps, which I suppose I don't need (It's an array, I'm should be able to adress my object by index)?
How do I update states `onChange` in an array of object in React Hooks
Another deep copy solution
https://codereview.stackexchange.com/questions/249405/react-hooks-update-array-of-object
The list goes on...
Basically I want the result I got without the extra line,
and if even possible:
- Without deep copying the state to inject back in.
- Without 3rd party libraries.
- Without using a keyed object.
- Without running a map/filter loop inside set_post.
Edit: The reason why map should be unnecessary in setPost.
In my particular scenario the Module that renders the getPost already is a map-loop. Trying to avoid nested loops.
(My logic simplified)
const [get_post, set_post] = useState([
{name: "First"},
{name: "Second"},
{name: "Third"},
])
//Render module
//Fixed numbers of controllers for each Object in Array.
get_post.map((post, index)=> {
<>
<button onClick={()=>
set_post([...get_post, get_post[index].content = "Content 1" ])}
}>
Controller 1
</button>
<button onClick={()=>
set_post([...get_post, get_post[index].content = "Content 2" ])}
}>
Controller 2
</button>
<button onClick={()=>
set_post([...get_post, get_post[index].content = "Content 3" ])}
}>
Controller 3
</button>
//...
</>
})
CodePudding user response:
If you just want to alter the first property, extract it from the array first.
You can use the functional updates method to access the current state value and return a new one with the changes you want.
set_post(([ first, ...others ]) => [{
...first,
content: "This is index zero"
}, ...others])
To alter any particular index, you can map the current array to a new one, creating a new object for the target index when you reach it
let x = the_target_index
set_post(posts => posts.map((post, i) => i === x ? {
...post,
content: `This is index ${x}`
} : post))
A slightly different version of this that matches what you seem to want to do in your answer would be
set_post(posts => {
posts[x] = { ...posts[x], content: `This is index ${x}` }
// or even something like
// posts[x].content = `This is index ${x}`
return [...posts] // clone the array
})
CodePudding user response:
I have found a solution that works!
I haven't seen this posted elsewhere so it might be interesting to look into.
To change or add a key/value to an object in an array by index just do this:
<button onClick={() => {
set_post( [...get_post , get_post[index].content = "my content" ] )
set_post( [...get_post ] ) //<-- this line removes the last input
}
}>
As Phil wrote, the earlier code is interpreted as:
const val = "This is index zero";
get_post[0].content = val;
get_post.push(val);
This seems to remove the latest get_post.push(val)
Can someone explain why this works?