DEV Community

Cathy Lai
Cathy Lai

Posted on

React Native Navigation: Why useEffect Doesn’t Run When You Go Back

Have you ever noticed that when you navigate back to a screen in React Native, your useEffect() doesn’t run again?

It feels like your screen is cached — and that’s partly true.

Let’s break down what’s really happening.


🎯 1. What actually happens when you push() vs replace()

In React Navigation (and Expo Router, which uses it under the hood):

  • navigation.push('Screen') → pushes a new instance of that screen onto the stack.
  • navigation.replace('Screen') → replaces the current screen with a new one (the old one unmounts).
  • When you go back, React Navigation just shows the previous screen again — it doesn’t remount it.

That’s why your useEffect(() => { ... }, []) only runs once — when the screen is first created.


🧠 2. Your screen is not cached — it’s still mounted

When you leave a screen using navigation.push(), the previous screen stays mounted in memory.

React Navigation keeps it alive so that going back is instant.

That means:

  • useEffect() with empty dependencies ([]) won’t run again.
  • Component state (e.g., form inputs) remains as it was.
  • cleanup from your effect won’t run either until the screen is unmounted.

If you want code to run every time the screen becomes visible again, you have to listen for focus events.


👀 3. Run code every time the screen comes into focus

You can use one of three approaches:

✅ Option 1: useFocusEffect

The most React-y way to handle this.

import { useFocusEffect } from '@react-navigation/native';
import { useCallback } from 'react';

useFocusEffect(
  useCallback(() => {
    console.log('Screen is focused');
    return () => console.log('Screen lost focus');
  }, [])
);
Enter fullscreen mode Exit fullscreen mode

Runs every time your screen is focused or blurred — without remounting.


✅ Option 2: Focus listener with navigation.addListener

useEffect(() => {
  const unsubscribe = navigation.addListener('focus', () => {
    console.log('Screen is focused again');
  });
  return unsubscribe;
}, [navigation]);
Enter fullscreen mode Exit fullscreen mode

This is the more “manual” version — same result, different style.


✅ Option 3: useIsFocused

Perfect if you just need a quick boolean to trigger effects.

import { useIsFocused } from '@react-navigation/native';

const isFocused = useIsFocused();

useEffect(() => {
  if (isFocused) {
    console.log('Screen is visible');
  }
}, [isFocused]);
Enter fullscreen mode Exit fullscreen mode

🔁 4. Forcing a remount (when you really want a fresh screen)

If you do want your screen to reset or useEffect to run fresh every time you navigate back:

<Stack.Screen
  name="MyScreen"
  options={{ unmountOnBlur: true }}
/>
Enter fullscreen mode Exit fullscreen mode

Now the screen will unmount when you leave and remount when you come back — so your normal useEffect will run again.

You can set this globally too:

<Stack screenOptions={{ unmountOnBlur: true }} />
Enter fullscreen mode Exit fullscreen mode

🧩 5. If your effect depends on route params

Sometimes you just want your effect to re-run when the route data changes.

const { id } = route.params ?? {};

useEffect(() => {
  fetchHouseDetails(id);
}, [id]);
Enter fullscreen mode Exit fullscreen mode

This ensures that if you navigate to the same screen with a different id, your effect re-runs automatically.


⚡️ Quick summary

Situation Recommended Solution
You want code to run only once useEffect(() => {}, [])
You want code to run every time the screen is visible useFocusEffect or useIsFocused
You want the screen to reset completely unmountOnBlur: true or use replace()
You want to handle new data Add route params to your effect dependencies

💡 Tip for Expo Router users

Everything above works the same — just import from "expo-router" instead of "@react-navigation/native".

Example:

import { useFocusEffect } from 'expo-router';
Enter fullscreen mode Exit fullscreen mode

🧭 Takeaway

Your React Native screens aren’t “cached” — they’re just still mounted.

Understanding this helps you control when your effects run, so you can keep your app responsive and predictable.

If your logic needs to refresh every time the user returns, don’t rely on useEffect.

Use focus-aware hooks — they’re your navigation superpower. 🚀


Thanks for reading! If you found this helpful, give it a ❤️ and follow for more React Native + Expo tips.

Top comments (0)