DEV Community

Lu Yu
Lu Yu

Posted on

Stale closure: Why did my setState only triggered twice?

I' trying to build a toast message API for React. My goal is to provide a fireNotification() api that can be called anywhere in the app and have React render the toast component.

I built this simple notification manager with sub/pub pattern and hope to be able to subscribe to new notifications in a useEffect hook

const notifications = [];

const listeners = new Set();

function subscribe(callback) {
  listeners.add(callback);
}

function publish() {
  listeners.forEach((cb) => {
    cb(notifications);
  });
}
export function fireNotification(content) {
  notifications.push(content);
  publish();
}

export default function App() {
  const [state, setState] = React.useState();
  React.useEffect(() => {
    subscribe((updated) => {
      setState(updated);
    });
  }, []);

  // state will be logged correctly 2 times
  // won't be updated after that
  console.log("state", state);
  return (
    <div className="App">
      <button onClick={() => {fireNotification('test')}}>fire</button>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

codesandbox

However, fireNotification() will only trigger setState twice

From the 3rd time onward, the state is not updated at all.

I'm able to make state update work by changing setState(updated) to setState([...updated]), but not sure why it works.

Can someone explain why setState(updated); only triggers twice? Thanks!

Top comments (0)