mtyson
mtyson

Reputation: 8580

React Cleanup async function fetch with hooks

This is in a functional component.

I have a submit() function that looks like so:

  async function handleSubmit(event) {
    event.preventDefault();
    try {
      let resp = await fetch("FOOBAR/BAX", {
        method: 'POST',
        body: JSON.stringify({ /*stuff*/})
      });
      if (resp.ok){
        // yadda yadda yadda
        props.history.push("/"); // navigate
      }
    } 
  } 

Now, when I cause navigation to occur I'm getting the dreaded 'Can't perform a React state update on an unmounted component.' error.

So, using effects, how do I make sure this fetch call is cleaned up? All the examples I'm seeing use useEffect to both set up and then cleanup the call (with cleanup function).

Upvotes: 0

Views: 508

Answers (1)

Drew Reese
Drew Reese

Reputation: 203512

Clean up a fetch request by cancelling on dismount using an abort controller

Factor the fetch request logic out of the handler into the effect hook and use a state hook to trigger the effect to fire. Return the controller's abort function in the effect hook to be called when the component unmounts.

const [body, setBody] = useState('');

useEffect(() => {
  const controller = new AbortController();
  const signal = controller.signal;

  if (body) {
    fetch("FOOBAR/BAX", {
      method: 'POST',
      body: JSON.stringify(body),
      signal, // add signal to request
    })
    .then(res => {
      setBody(''); // clear request body value
      if (res.ok) props.history.push('/');
    });
  }

  return controller.abort; // return the abort function to be called when component unmounts
}, [body]);

const handleSubmit = (event) => {
  event.preventDefault();
  setBody({ /*stuff*/ }); // set request body value to trigger effect to fetch
};

Here's a codesandbox with this implemented as a react hook, with manual abort and automatic abort on unmounting.

Upvotes: 1

Related Questions