Oliver Ilmjärv
Oliver Ilmjärv

Reputation: 341

multiple react state updates renders only the last update

I made a little function to add custom alerts to the state array, and the view renders them. The problem is, if i call the function twice in a row, the first alert is rendered only for a moment, and then its replaced with the second alert. When i call the method with a mouse click, the function works correctly.

I have tried to apply some waiting before pushing to the array list, but no luck with that.

const Element = () => {
  const [alerts, setAlerts] = React.useState([])

  const addAlert = (data) => {
        setAlerts([...alerts, <CustomAlert key={alerts.length} message={data.message} color={data.color} />])
    }

  return (
    <div>
        <button onClick={() => {
                    // this renders only the last state update.
                    addAlert({message: "test", color: "error"}); 
                    addAlert({message: "2", color: "error"})
                }
            }>
           add alert button 
        </button>
      <div>
        {alerts}
      </div>
    </div>
  );
}

Upvotes: 1

Views: 590

Answers (2)

Osama Sayed
Osama Sayed

Reputation: 2023

React updates the state asynchronously. This means when you are updating the state 2 times in a row, accessing the value of alerts directly might not have the latest inserted item. You should use a function instead when calling setAlerts:

const [alerts, setAlerts] = React.useState([]);

  const addAlert = (data) => {
    setAlerts((prevAlerts) => {
      const newAlerts = [...prevAlerts];
      newAlerts.push(
        <CustomAlert
          key={alerts.length}
          message={data.message}
          color={data.color}
        />
      );
      return newAlerts;
    });
  };

  return (
    <div>
      <button
        onClick={() => {
          // this renders only the last state update.
          addAlert({ message: "test", color: "error" });
          addAlert({ message: "2", color: "error" });
        }}
      >
        add alert button
      </button>
    </div>
  );

Upvotes: 2

Axnyff
Axnyff

Reputation: 9964

alerts in your code refers to the value of the current render in both case so your addAlert won't work. To fix this, you can use the setter version with a function:

setAlerts(currentAlerts => [...currentAlters, <CustomAlert key={alerts.length} message={data.message} color={data.color} />])

Upvotes: 1

Related Questions