69

Perviously when I wanted to make some actions when screen is opened I put them inside componentDidMount. For example I can fetch some data.

like this.

componentDidMount() {
  this.updateData();
}

But with react-navigation componentDidMount occurs only one time when user open screen first time, and if later user open this page again it will not trigger componentDidMount.

What is proper way to detect when page(screen) is activated and do actions?

tuledev
  • 10,177
  • 4
  • 29
  • 49
rendom
  • 3,417
  • 6
  • 38
  • 49

6 Answers6

155

React navigation v6

Fetching data with an API call when a screen becomes focused

React Navigation provides a hook that runs an effect when the screen comes into focus and cleans it up when it goes out of focus. This is useful for cases such as adding event listeners, for fetching data with an API call when a screen becomes focused, or any other action that needs to happen once the screen comes into view.

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

function ProfileScreen() {
  useFocusEffect(
    React.useCallback(() => {

      alert('Screen was focused');

      return () => {

        alert('Screen was unfocused');
        // Useful for cleanup functions

      };
    }, [])
  );

  return <View />;
}

A boolean indicating whether the screen is focused or not

React Navigation provides a hook that returns a boolean indicating whether the screen is focused or not. The hook will return true when the screen is focused and false when our component is no longer focused. This enables us to render something conditionally based on whether the user is on the screen or not.

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

function Profile() {
  const isFocused = useIsFocused();

  return <Text>{isFocused ? 'focused' : 'unfocused'}</Text>;
}

Ref v6.x: https://reactnavigation.org/docs/function-after-focusing-screen/


Older:

With react-navigation, you can do that in 2 steps:

  1. Add listeners in componentDidMountor componentWillMount, to hook events

  2. Remove listeners in componentWillUnmount , to avoid unexpected calling

API Reference Documents v3.x, v4.x, v5.x

React-navigation v3.x, v4.x:

addListener - Subscribe to updates to navigation lifecycle

React Navigation emits events to screen components that subscribe to them:

  • willFocus - the screen will focus
  • didFocus - the screen focused (if there was a transition, the transition completed)
  • willBlur - the screen will be unfocused
  • didBlur - the screen unfocused (if there was a transition, the transition completed)

React-navigation 3.x, 4.x example:

const didBlurSubscription = this.props.navigation.addListener(
  'didBlur',
  payload => {
    console.debug('didBlur', payload);
  }
);

// Remove the listener when you are done
didBlurSubscription.remove();

Ref v4.x https://reactnavigation.org/docs/4.x/navigation-prop/#addlistener---subscribe-to-updates-to-navigation-lifecycle

UPDATED v5.x

The events had been changed in v5.x

Screens can add listeners on the navigation prop like in React Navigation. By default, there are 2 events available:

  • focus - This event is emitted when the screen comes into focus
  • blur - This event is emitted when the screen goes out of focus
  • state (advanced) - This event is emitted when the navigator's state changes

Sample code from reactnavigation.org

class Profile extends React.Component {
  componentDidMount() {
    this._unsubscribe = navigation.addListener('focus', () => {
      // do something
    });
  }

  componentWillUnmount() {
    this._unsubscribe();
  }

  render() {
    // Content of the component
  }
}

Use with hook

function Profile({ navigation }) {
  React.useEffect(() => {
    const unsubscribe = navigation.addListener('focus', () => {
      // do something
    });

    return unsubscribe;
  }, [navigation]);

  return <ProfileContent />;
}

Listeners prop on Screen

<Tab.Screen
  name="Chat"
  component={Chat}
  listeners={({ navigation, route }) => ({
    tabPress: e => {
      // Prevent default action
      e.preventDefault();

      // Do something with the `navigation` object
      navigation.navigate('AnotherPlace');
    },
  })}
/>

Ref v5.x: https://reactnavigation.org/docs/navigation-events

tuledev
  • 10,177
  • 4
  • 29
  • 49
  • 4
    I do this. But it only calls once. when I go to another app and came to my app I need to call function again. What should I do? – Amir Shabani Oct 14 '19 at 08:04
  • 1
    Opening app from background won't invoke `didFocus`. You should use ` AppState.addEventListener('change', this.handleAppStateChange);`. – tuledev Oct 14 '19 at 08:40
  • How do you remove listener on version 5.x? – The1993 Feb 27 '20 at 10:06
  • willFocus, didFocus, willBlur & didBlur how to achieve this with react-navigation v5? – Kailash Uniyal May 26 '20 at 13:34
  • As their documents, v5.x has `focus` and `blur` event. For `will`, `did` event, you can try `state (advanced)` – tuledev May 27 '20 at 06:36
  • WOW I did not know these hooks existed and I have built quite a few front ends with React Navigation V6, these are super useful in certain situations, much better dev experience than using event listeners in a use effect hook. – ICW May 25 '22 at 19:09
15

FOR V3.x and V4.x This might be late but this is how I solved it. See the code below. Don't forget to import withNavigation and wrap your export default withNavigation.

import { withNavigation } from "react-navigation";
 componentDidMount() {
    const { navigation } = this.props;
    this.focusListener = navigation.addListener("didFocus", () => {
      // The screen is focused
      // Call any action
    });
  }

  componentWillUnmount() {
    // Remove the event listener
    this.focusListener.remove();
  }

export default withNavigation(Your Class Name);

FOR V5.x Please use this.

 componentDidMount(){
    const { navigation } = this.props;
    this.focusListener = navigation.addListener("focus", () => {      
    // Call ur function here.. or add logic.     
    });
}
Armar
  • 249
  • 3
  • 7
  • Didn't work like you described it (for v5). There seems not to be any `didFocus` event. See here for detail information: https://reactnavigation.org/docs/navigation-events/#navigationaddlistener. So https://stackoverflow.com/a/50290888/1256697 is the right answer here. – suther Jun 22 '20 at 11:02
  • Hi this worked for v3 and v4 but for v5 am using this. Alot of things changed in v5 so you might want to make the Documentation your friend. `const { navigation } = this.props; this.focusListener = navigation.addListener("focus", () => { // Call ur function here.. or add logic. });` – Armar Jun 23 '20 at 12:06
  • Yep, I know how it works (because I'm currently working with v5), but I add the comment for other users so that they can save some time. Would be fine, if you edit your post and write it inside, that you're answer depend on v3 & v4 – suther Jun 23 '20 at 12:33
  • @suther i have updated my answer, Thanx for the concern. – Armar Jun 24 '20 at 13:14
8

componentDidMount / componentWillUnmount does not work in all cases of navigation (like tabs).

You need to use addListener with events didFocus and didBlur to make such actions. See documentation for details

Samuli Hakoniemi
  • 18,740
  • 1
  • 61
  • 74
7

The latest version of react-navigation provides custom hooks that let you react to focus and blur events: https://reactnavigation.org/docs/function-after-focusing-screen.

The following is an example from their docs that uses the onFocusEffect hook to make an API call on focus (it cleans up the effect when the screen goes out of focus):

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

function Profile({ userId }) {
  const [user, setUser] = React.useState(null);

  useFocusEffect(
    React.useCallback(() => {
      const unsubscribe = API.subscribe(userId, user => setUser(data));

      return unsubscribe;
    }, [userId])
  );

  return <ProfileContent user={user} />;
}
4

NavigationEvents is another way to add event listener just inside JSX. See documentation for details.

It looks something like this:

import { NavigationEvents } from 'react-navigation';

return (
  <ScrollView style={styles.container}>
    <NavigationEvents            
      onDidBlur={payload => console.log('did blur', payload)}
    />
    {this.props.children}
  </ScrollView>
);
Dzmitry
  • 215
  • 1
  • 3
  • 8
0

in react navigation 6 all you need to do is

import { useIsFocused } from '@react-navigation/native';
function Profile() {
  const isFocused = useIsFocused();

  return <Text>{isFocused ? 'focused' : 'unfocused'}</Text>;
}

refer docs