简体   繁体   中英

Alter Object Array in React with useState-hook without deep copying

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>

//...

</>
})

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

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_modules( [...get_modules , get_modules[index].content = "my content" ] )
      set_modules( [...get_modules ] ) //<-- 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?

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