Search code examples
javascriptreactjsreduxasync-awaites6-promise

Why react doesn't update state in "then" promise?


I have a table with Delete button in each row. When you delete the row, the Client will send request do the API asynchronously. Which means, that if you delete multiple rows quickly, you don't have to wait for one request to finish, to send another Delete request. When you click Delete button and request was send, the button will be disabled (before the response comes back and the row will disappear).

This is the function that handles delete in the Container:

  const [deletingTeams, setDeletingTeams] = useState([]);

  const handleDelete = (teamId) => {
    setDeletingTeams([...deletingTeams, teamId]);

    deleteTeam(teamId).then(() => {    
      console.log(deletingTeams); <---- This prints EMPTY array. Why????   
      let newState = deletingTeams.filter((id) => id !== teamId);
      setDeletingTeams(newState);
    });
  };

Very simple. deletingTeams is an array that holds ids of teams that are being deleted at the moment (the response didn't comeback from that API, that deletion was successful).

In line 3 (setDeletingTeams([...deletingTeams, teamId]);) I'm adding new teamId to the array, and it works. New array is being passed to the List component and Delete button is indeed disabled. BUT...

...when response comes back, in then promise, I want to remove that teamId from the array. The problem is, that deletingTeams array is already empty (before the filter). Why??

Thanks for the explanation,


Solution

  • State updates are async - you are not guaranteed to see latest value of the updated state straight away.

    Use the callback approach to filter and set the state.

    const [deletingTeams, setDeletingTeams] = useState([]);
    
    const handleDelete = (teamId) => {
      setDeletingTeams([...deletingTeams, teamId]);
    
      deleteTeam(teamId).then(() => {
        // console.log(deletingTeams); //<---- state updates are async - you will see the latest value of the state in the next re-render
        // let newState = deletingTeams.filter((id) => id !== teamId); //<---- remove this
        setDeletingTeams((prev) => prev.filter((id) => id !== teamId)); //<--- use call back approach to set the state
      });
    };