0
votes

I have a React Native app that has been built using Expo (v35.0.0). I have a simple async function (loadData()) that runs a fetch (via fetchData()) request to an API that the response is then passed into my redux store:

const loadData = async (id, token) => {
  setIsLoading(true);
  try {
    await dispatch(fetchData(id, token));
  } catch (error) {
    setHasError(true);
  }
  setIsLoading(false);
};

useEffect(() => {
  loadData(user.client.id, user.token);
}, [user.client.id]);

However, when the user logs out we are presented with the warning: "Can't perform a React state update on an unmounted component", which I understand is because the async request has been cancelled.

I have attempted to implement the AbortController approach (as outlined in this article: https://dev.to/iquirino/react-hook-clean-up-useeffect-24e7), however then we are presented with an error stating AbortConroller is unknown.

I thought that support for AbortConroller would be within Expo now as it was added to React Native back in July last year as part of the v0.60 release.

So, is it possible to implement AbortController within Expo to cancel the async request? If not, how should I go about cancelling the request to avoid the warning (& memory leak)?

1
In your useEffect hook, you can return a cleanup function, with this you can set a flag, and then if it's set abort instead of calling setState.. - Keith

1 Answers

1
votes

Because your dispatch is async, it's possible for it to finish after your component is unmounted.

Inside useEffect you can return a function that you call to cleanup. Here you can set a flag to indicate the component is no longer mounted, and if this flag indicates it's no longer mounted, you can tell it not to update state.

eg.

let mounted = true;

const loadData = async (id, token) => {
  setIsLoading(true);
  try {
    await dispatch(fetchData(id, token));
  } catch (error) {
    if (mounted) setHasError(true);
  }
  if (mounted) setIsLoading(false);
};

useEffect(() => {
  loadData(user.client.id, user.token);
  return () => mounted = false;
}, [user.client.id]);