30
votes

I am using react-native with react-native-navigation. I would like to reload data when a component is shown. The component is shown when a user clicks on a tab navigation button.

Should I use react life cycle events or is there something in react-native-navigation that can trigger a function when a user navigates back to a component?

I am using redux, I am not sure if that could be used to help?

This issue refers to onDisplay which seems like what I am looking for. However I can't find any official documentation about it - https://github.com/wix/react-native-navigation/issues/130

8
Basically attached custom event lets say 'loaded' to elements and trigger event loaded when ever needed - mymotherland
Yes, but how do I know when the component is being displayed? - Daryn
I am not familiar in react. May this link helps you brewhouse.io/blog/2015/03/24/… - mymotherland
Thanks but this is to do with react-native and react-native-navigation so I don't think that helps... - Daryn
@ChrisF Can you please restore the answer you deleted here? Had I seen it, it would have saved me hours of time I wasted before I stumbled on that GitHub PR the answerer linked to. - James Ko

8 Answers

5
votes

include this as 'callIfBackToThisRoute'...

export default ( props, call ) => {
    if( !props.navigation ) throw 'I need props.navigation'
    const thisRoute = props.navigation.state.routeName;
    props.navigation.addListener(
        'willFocus',
        payload => {
            if( payload.state.routeName == thisRoute) call(props)
        }
    );
}

and use it inside your component...

componentDidMount() {
    const { doIt } = this.props;
    doIt()
    callIfBackToThisRoute(
        this.props,
        (props) => doIt()
    )
}
37
votes

I like the solution proposed by Bruno Reis. I tweaked mine to make it a bit simpler.

class Whatever extends Component {
    componentDidMount(){
        this.load()
        this.props.navigation.addListener('willFocus', this.load)
    }
    load = () => {
        ...
    }

}
3
votes

For RNN v3, after trying out for quite a couple times, I finally figured out the correct way:

  componentDidMount() {
    this.navigationEventListener = Navigation.events().bindComponent(this);
  }

  componentWillUnmount() {
    if (this.navigationEventListener) {
      this.navigationEventListener.remove();
    }
  }

  componentDidAppear() {   // Lazy loading data for RNN
    if (this.state.routes.length === 0) {
      this.getData();
    }
  }

The key is that, the binding of event listener is mandatory, otherwise the componentDidAppear() and componentDidDisappear() won't be triggered.

2
votes

This is what I ended up using:

export default class RootNavigator extends React.Component {
  state = {currentScreen: null}

  _onNavigationStateChange(prevState, newState, action) {
    // const currentScreen = getCurrentRouteName(currentState)
    // const prevScreen = getCurrentRouteName(prevState)
    // console.debug('onNavigationStateChange currentScreen=', currentScreen,
    //   'prevScreen=', prevScreen, 'action.routeName=', action.routeName)
    console.debug('onNavigationStateChange action.routeName=', action.routeName)
    this.setState({currentScreen: action.routeName})
  }

  render() {
    return <RootStackNavigator onNavigationStateChange={this._onNavigationStateChange.bind(this)}
      screenProps={{currentScreen: this.state.currentScreen}}/>;
  }

coupled with componentDidUpdate() on the screen (which may or may not perform something depending on the screenProps's currentScreen).

Note: I don't know what/where getCurrentRouteName() is and it gave error for me so I don't use it and use action.routeName directly.

See https://github.com/react-community/react-navigation/issues/2371 and https://github.com/react-community/react-navigation/issues/51#issuecomment-323536642 for more information and discussion.

2
votes

In the documentation, it shows that you can add a focus event like this:

import React, { useEffect } from 'react'

const MyComponent = ({ navigation }) => {

    useEffect(() => {
        const unsubscribe = navigation.addListener('focus', () => {
            // do something
            console.log('Hello World!')
        });
        return unsubscribe;
    }, [navigation]);

    return (
        <View>
            <Text>MyComponent</Text>
        </View>
    )
}

export default MyComponent
1
votes

The currently accepted answer suggests a react-navigation solution, not react-native-navigation (RNN), so I'll go ahead and give my two cents.

As Stephen Liu points out in his answer, RNN provides screen lifecycle methods that fire when a component appears (componentDidAppear) and disappears (componentDidDisappear).

Stephen's answer works for a class component, however in the age of hooks I prefer function components. So this is how to use RNN's screen lifecycle methods in a function component:

import React, { useEffect } from 'react'
import { Navigation } from 'react-native-navigation'

const MyComponent = ({ componentId }) => {

  useEffect(() => {
    const navListener = Navigation.events().bindComponent(this, componentId)

    // remove the listener during cleanup
    return () => {
      navListener.remove()
    }
  }, [componentId])

  this.componentDidAppear = () => {
    // do stuff when component appears
  }

  this. componentDidDisappear = () => {
    // do stuff when component disappears
  }

}

Important: MyComponent needs a componentId prop, which is injected automatically if it's a registered RNN screen or modal (Navigation.registerComponent). You can also manually pass it down from a screen component to the child where you need it.

🌟🌟🌟Bonus: useComponentDidAppear hook 🌟🌟🌟

I use RNN's componentDidAppear fairly often in my project, so I made a custom hook to reuse it super easily throughout my function components:

export const useComponentDidAppear = (componentId, callback) => {
  useEffect(() => {
    const navListener = Navigation.events().bindComponent(this, componentId)
    return () => {
      navListener.remove()
    }
  }, [componentId])

  this.componentDidAppear = () => {
    callback()
  }
}

// then use it like this
const SomeScreen = ({ componentId }) => {

  useComponentDidAppear(componentId, () => {
    // do stuff when the component appears!
  })

}
0
votes

i have faced the same situation in one of my own project and use the useFocusEffect hook by react navigation to solve it

for more info you can refer to the docs about this

https://reactnavigation.org/docs/function-after-focusing-screen#triggering-an-action-with-the-usefocuseffect-hook

-4
votes

Override componentWillMount lifecycle method: https://facebook.github.io/react/docs/component-specs.html#mounting-componentwillmount Or just put the functionality inside render method