Always cancel promises started in React.useEffect

December 11, 2021 React JavaScript Promise asynchronous programming

If you start a promise inside useEffect, make sure to “cancel” it in the cleanup handler.

const [product, setProduct] = React.useState(null);

React.useEffect(() => {
  // use a local variable to track effect state - not refs and certainly not component state
  let cancelled = false;
  fetchProduct(productID).then((p) => {
    if (cancelled) {
      return;
    }
    setProduct(p);
  });
  return () => {
    cancelled = true;
  };
}, [productID]);

You do this because you don’t know when the promise will resolve.

Would it resolve after the component has been unmounted? That’s certainly possible - and a top cause of “Update on an unmounted component” errors.

Would a stale promise resolve after the fresh promise, when you update the effect’s dependencies? Also possible.

There’s no facility to cancel a promise. The handler will fire regardless of component state. But you can discard the handler, and then it doesn’t matter. For this, we track the effect’s state with a variable. A simple local variable is enough here, and don’t use anything connected to component lifecycle like state or ref.

Obviously, there are some libraries to abstract all of this away. But don’t do this enough to warrant abstraction. Isolate state management from component lifecycle. Use Redux.

Buy me a coffee Liked the post? Treat me to a coffee