`componentDidMount()` function is not called after navigation
Asked Answered
B

11

30

I am using stackNavigator for navigating between screens. I am calling two API's in componentDidMount() function in my second activity. When i load it first time, it gets loaded successfully. Then i press back button to go back to first activity. Then, if i am again going to second activity, the APIs are not called and I get render error. I am not able to find any solution for this. Any suggestions would be appreciated.

Bridgid answered 29/12, 2017 at 5:55 Comment(3)
componentDidMount is called only once in its lifetime.Please check componentWillReceiveProps methodPropositus
If you go back with navigate.goBack(), componentDidMount() should happen again when you navigate to it once more. (just confirmed it in my code). Did you handle go back with special way? Suggest to try it out with simpler code.Crescendo
I have not handled goBack() in a special way. I am pressing the back arrow button which is on the top let corner of screen.Bridgid
H
52

If anyone coming here in 2019, try this:

import {NavigationEvents} from 'react-navigation';

Add the component to your render:

<NavigationEvents onDidFocus={() => console.log('I am triggered')} />

Now, this onDidFocus event will be triggered every time when the page comes to focus despite coming from goBack() or navigate.

Hamburger answered 20/11, 2018 at 7:4 Comment(3)
I suggest onWillFocus, as it will not cause any delay. Did focus might cause a slight delay viewable to the user.Optics
no one is working for me onDidFocus and onWillFocus .I am using createDrawerNavigator.Filmore
where to use NavigationEvents? It's giving me an error "withnavigation can only be used on a view hierarchy of a navigator"Butterandeggs
D
17

If the upvoted syntax that uses NavigationEvents component is not working, you can try with this:

// no need to import anything more

// define a separate function to get triggered on focus
onFocusFunction = () => {
  // do some stuff on every screen focus
}

// add a focus listener onDidMount
async componentDidMount () {
  this.focusListener = this.props.navigation.addListener('didFocus', () => {
    this.onFocusFunction()
  })
}

// and don't forget to remove the listener
componentWillUnmount () {
  this.focusListener.remove()
}
Diorama answered 1/5, 2019 at 21:13 Comment(0)
T
8

The React-navigation documentation explicitly described this case:

Consider a stack navigator with screens A and B. After navigating to A, its componentDidMount is called. When pushing B, its componentDidMount is also called, but A remains mounted on the stack and its componentWillUnmount is therefore not called.

When going back from B to A, componentWillUnmount of B is called, but componentDidMount of A is not because A remained mounted the whole time.

Now there is 3 solutions for that:

Subscribing to lifecycle events

...

React Navigation emits events to screen components that subscribe to them. There are four different events that you can subscribe to: willFocus, willBlur, didFocus and didBlur. Read more about them in the API reference.

Many of your use cases may be covered with the withNavigationFocus higher-order-component, the <NavigationEvents /> component, or the useFocusState hook.

  1. the withNavigationFocus higher-order-component
  2. the <NavigationEvents /> component
  3. the useFocusState hook (deprecated)

withNavigationFocus higher-order-component

import React from 'react';
import { Text } from 'react-native';
import { withNavigationFocus } from 'react-navigation';

class FocusStateLabel extends React.Component {
  render() {
    return <Text>{this.props.isFocused ? 'Focused' : 'Not focused'}</Text>;
  }
}

// withNavigationFocus returns a component that wraps FocusStateLabel and passes
// in the navigation prop
export default withNavigationFocus(FocusStateLabel);

<NavigationEvents /> component

import React from 'react';
import { View } from 'react-native';
import { NavigationEvents } from 'react-navigation';

const MyScreen = () => (
  <View>
    <NavigationEvents
      onWillFocus={payload => console.log('will focus', payload)}
      onDidFocus={payload => console.log('did focus', payload)}
      onWillBlur={payload => console.log('will blur', payload)}
      onDidBlur={payload => console.log('did blur', payload)}
    />
    {/*
      Your view code
    */}
  </View>
);

export default MyScreen;

useFocusState hook

First install library yarn add react-navigation-hooks

import { useNavigation, useNavigationParam, ... } from 'react-navigation-hooks'

function MyScreen() {   const focusState = useFocusState();   return <Text>{focusState.isFocused ? 'Focused' : 'Not Focused'}</Text>; }

Here is my personal solution, using onDidFocus() and onWillFocus() to render only when data has been fetched from your API:

import React, { PureComponent } from "react";
import { View } from "react-native";
import { NavigationEvents } from "react-navigation";

class MyScreen extends PureComponent {
  state = {
    loading: true
  };

  componentDidMount() {
    this._doApiCall();
  }

  _doApiCall = () => {
    myApiCall().then(() => {
      /* Do whatever you need */
    }).finally(() => this.setState({loading: false}));
  };

  render() {
    return (
      <View>
        <NavigationEvents 
              onDidFocus={this._doApiCall} 
              onWillFocus={() => this.setState({loading: true})}
        />
        {!this.state.loading && /*
        Your view code
        */}
      </View>
    );
  }
}

export default MyScreen;
Trix answered 19/12, 2019 at 9:45 Comment(0)
T
5

Solution for 2020 / React Navigation v5

Inside your screen's ComponentDidMount

componentDidMount() {
    this.props.navigation.addListener('focus', () => {
      console.log('Screen.js focused')
    });
}

https://reactnavigation.org/docs/navigation-events/

Alternatively: Put the addListener method in constructor instead to prevent duplicated calls

Tritium answered 28/6, 2020 at 19:23 Comment(0)
C
4

React-navigation keeps the component mounted even if you navigate between screens. You can use the component to react to those events :

<NavigationEvents
  onDidFocus={() => console.log('hello world')}
/>

More info about this component here.

Chaudfroid answered 30/10, 2018 at 10:16 Comment(0)
S
2

According to react-navigation docs we can use as below

componentDidMount () {
  this.unsubscribe= this.props.navigation.addListener('focus', () => {
    //Will execute when screen is focused
  })
}

componentWillUnmount () {
  this.unsubscribe()
}

Similar to vitosorriso`s answer but should changed didFocus to focus according to docs

Saurischian answered 20/5, 2020 at 16:36 Comment(0)
A
1

You want to perform something after every time navigating to a component using drawernavigator or stacknavigator ; for this purpose NavigationEvents fits better than componentDidMount .

import {NavigationEvents} from "react-navigation";
<NavigationEvents onDidFocus={()=>alert("Hello, I'm focused!")} />

Note : If you want to do the task every time after focusing on a component using drawer navigation or stack navigation then using NavigationEvents is better idea. But if you want to do the task once then you need to use componenetDidMount .

Absentminded answered 28/1, 2019 at 17:1 Comment(0)
R
1
//na pagina que você quer voltar
import {NavigationEvents} from 'react-navigation';

async atualizarEstado() {
  this.props.navigation.setParams({
  number: await AsyncStorage.getItem('count'),
});}

render() {
return (
  <View style={styles.container}>
    <NavigationEvents onDidFocus={() => this.atualizarEstado()} />
  </View>
);
}
Radiocommunication answered 28/5, 2020 at 14:5 Comment(2)
Hope It will solve issue but please add explanation of your code with it so user will get perfect understanding which he/she really wants.Wagers
also.. really writing your code in your native language? so 1990's ;-)Aircrew
D
1

I have face this issue, the problem is when you navigate a page, the first time it call constructor, componentWillmount, render componentDidmount, but in second time when navigate to the same page it only call render, so if you do any API call or something from componentDidmount it would not be called,

and also componentWillunmount never called.

You can use this method, if you are using react-navigation 5.x with class component, it can solve your problem.

for every class component page add this method and call this method once from the constructor

constructor(props) {
    super(props);
    
    this.state = {
        ...
    };
    ...
    this.navigationEventListener(); // call the function
 }



navigationEventListener = () => { // add this function
        let i = 0;
        const initialState = this.state;
        this.props.navigation.addListener('focus', () => {
            if (i > 0) {
                this.setState(initialState, () => {
                    //this.UNSAFE_componentWillMount(); // call componentWillMount 
                    this.componentDidMount(); // call componentDidMount
                });
            }
        });
        this.props.navigation.addListener('blur', () => {
            this.componentWillUnmount();  //call componentWillUnmount
            ++i;
        });
    }
Dario answered 15/6, 2021 at 13:54 Comment(1)
Yeah bro you save my code \o/ ty!Interceptor
D
0

https://reactnavigation.org/docs/navigation-events/

useEffect(() => {       
            
    const unsubscribe = props.navigation.addListener('focus', () => {
        // do something
        // Your apiCall();
    });

return unsubscribe;
    
}, [props.navigation]);
Downtrodden answered 31/1, 2021 at 16:33 Comment(0)
B
-1

In React, componentDidMount is called only when component is mounted.I think what you are trying to do is call your API on going back in StackNavigator. You can pass a callback function as parameter when you call navigate like this on Parent Screen:

  navigate("Screen", {
     onNavigateBack: this.handleOnNavigateBack
  }); 
  handleOnNavigateBack = () => {//do something};

And on Child Screen

this.props.navigation.state.params.onNavigateBack();
this.props.navigation.goBack();
Blabber answered 29/12, 2017 at 8:38 Comment(3)
Thanks for reply @suyog .. Is there any method so that document renders only after all the API have been called.Bridgid
in your render method just check if (!prop1FromApi || !prop2FromApi) return null;Eucalyptol
You can set state of the activity for example loading : false and show loading icon in render function if loading is false. Then you can set loading to be true after api is called.Blabber

© 2022 - 2024 — McMap. All rights reserved.