React State During Update

In one of the beginning React tutorials we’re updating a list of users optimistically and then using axios to POST a new user. Here’s the code in question:

const addUser = () => {
    const newUser = { id: 0, name: "mosh" };
    setUsers([newUser, ...users]);

    axios
      .post("https://jsonplaceholder.typicode.com/users", newUser)
      .then((res) => setUsers([res.data, ...users]));
  };

So we call setUsers([newUser, ...users]); to update the list optimistically, then call axios.post, and then call setUsers(...) again. My question is why isn’t there a duplicate user in the list? I would think after the first setUsers that we’d have the mosh user in the list, then on the result of the POST we add the user (res.data) again to the list so there would be two mosh users, yet only one is in the list.

From my understanding of it, the first setUsers() is your optimistic State. You just set frontend users list. It never goes to the backend.

Then you’re making the backend POST. Once that successfully completes, the next setUsers() call is essentially overwriting the current users list with the results from the POST.

Does that help?

When the first call to setUsers([newUser, ...users]) happens, newUser is prepended to the current list of users in React state. It doesn’t go to the backend since we’re not making any kind of HTTP request with it. It’s stored in state handled by React which is what is reflected in the UI.

When the POST request is made the server response returns just that one user that was created on the server, not a list. So the setUsers([res.data, ...users]) prepends res.data to the list stored by the state in React, which is where I’m confused. I would have thought the current list in state contains newUser, which was prepended earlier, which would include the same User object as res.data, minus the new id that the server added. It seems like the function retains a reference to the initial users list rather than the list after adding newUser.

I think you’re right. I didn’t find the time yet to investigate that further but currently I believe the arrow function passed to .then() and the users array form a closure.

1 Like

From what I’ve been able to find it does sound like the behavior of a stale closure. Sounds like this is usually undesired behavior but in this case it works to our advantage.