0

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).

mtyson
  • 8,196
  • 16
  • 66
  • 106
  • 3
    It doesn't look like there's any state change in your `handleSubmit` function. I don't see anything to clean here. Would you care to share more code, maybe on stackblitz? – yonki Jan 04 '20 at 21:55
  • @yonki Thanks, maybe my assumption is wrong. – mtyson Jan 04 '20 at 21:57
  • @yonki let's say I want to make sure that request is cancelled -- how would I useEffect to do that? – mtyson Jan 04 '20 at 22:21
  • 2
    @mtyson I believe @yonki is informing you that the error doesn't match the code you shared. Your `handleSubmit` function doesn't appear to update state (unless it is hidden within the yadda yadda yadda bit). About using `useEffect` hook to cancel a fetch request you can use an [abort controller](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) with the fetch request. Let us know if you need help with that. In the mean time , as yonki requested, can you share more of the component code or share a link to it in stackblitz or codesandbox? – Drew Reese Jan 04 '20 at 23:58

1 Answers1

1

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.

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • 1
    @mtyson Thank you, that is very kind of you to say. I just noticed and fixed a typo I had in the effect hook though, the dependency should be on `body`, not `request` that I had initially called that variable. Hopefully that didn't trip you up. – Drew Reese Jan 05 '20 at 17:53