React Native useEffect API call runs twice after app returns from background on Android


I'm facing a strange issue in a React Native app where an API request sometimes gets triggered twice after the app comes back from the background.

It seems to happen mostly on Android. On the initial screen load everything works normally.

I have a screen where I fetch the latest data when the screen is focused:

const fetchData = async () => {
  try {
    console.log('API CALLED');

    const response = await api.get('/dashboard');

    setData(response.data);
  } catch (error) {
    console.log('API error:', error);
  }
};

useFocusEffect(
  useCallback(() => {
    fetchData();

    return () => {
      console.log('Screen unfocused');
    };
  }, [])
);

Normally I get:

API CALLED

only once.

But if I:

  1. Open this screen

  2. Put the app in the background

  3. Wait for a few seconds

  4. Open the app again

I sometimes see:

API CALLED
API CALLED

and two requests appear in the network logs almost at the same time.

I initially thought the component was being mounted twice, so I added:

useEffect(() => {
  console.log('MOUNTED');

  return () => {
    console.log('UNMOUNTED');
  };
}, []);

But when the duplicate request happens, I don't always see the component unmounting/remounting.

I also checked that fetchData() isn't being called from another useEffect.

Environment

React Native: 0.81.x
React Navigation: 7.x
React: 19.x
Platform: Android

What I tried

I can prevent the duplicate request with a ref:

const isFetching = useRef(false);

const fetchData = async () => {
  if (isFetching.current) return;

  isFetching.current = true;

  try {
    const response = await api.get('/dashboard');
    setData(response.data);
  } finally {
    isFetching.current = false;
  }
};

This works as a guard, but I'm not sure if I'm just hiding the actual problem.

Can useFocusEffect run again when an Android app returns from the background even if the screen was never unmounted?

If so, what is the recommended pattern when I want to fetch once when navigating to the screen, but don't want duplicate requests when the app resumes?

0
Aug 23 at 4:04 PM
User AvatarNirmal Patel
#android#react-native#react-hooks#react-navigation

Accepted Answer

This can happen on Android when the app goes between background and foreground. useFocusEffect may run again when the screen regains focus, so if the previous request is still running, you can end up with two requests.

If you want to fetch whenever the screen gets focus, I'd cancel the previous request when the effect is cleaned up:

useFocusEffect(
  useCallback(() => {
    const controller = new AbortController();

    const fetchData = async () => {
      try {
        console.log('API CALLED');

        const response = await api.get('/dashboard', {
          signal: controller.signal,
        });

        setData(response.data);
      } catch (error: any) {
        if (error.name !== 'CanceledError' && error.name !== 'AbortError') {
          console.log('API error:', error);
        }
      }
    };

    fetchData();

    return () => {
      controller.abort();
    };
  }, [])
);

If you only want the request to run when the component mounts, use a normal useEffect instead:

useEffect(() => {
  let isMounted = true;

  const fetchData = async () => {
    try {
      const response = await api.get('/dashboard');

      if (isMounted) {
        setData(response.data);
      }
    } catch (error) {
      console.log('API error:', error);
    }
  };

  fetchData();

  return () => {
    isMounted = false;
  };
}, []);

For a larger app, I'd probably use something like TanStack Query rather than handling caching, deduplication, and refetching manually.

User Avataruser28224355
Aug 23 at 4:17 PM
-2