41

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

Alex Wayne
  • 178,991
  • 47
  • 309
  • 337
Daryn
  • 3,394
  • 5
  • 30
  • 41
  • Basically attached custom event lets say 'loaded' to elements and trigger event loaded when ever needed – mymotherland Oct 17 '16 at 12:02
  • Yes, but how do I know when the component is being displayed? – Daryn Oct 17 '16 at 12:07
  • I am not familiar in react. May this link helps you http://brewhouse.io/blog/2015/03/24/best-practices-for-component-state-in-reactjs.html – mymotherland Oct 17 '16 at 12:18
  • Thanks but this is to do with react-native and react-native-navigation so I don't think that helps... – Daryn Oct 17 '16 at 12:20
  • @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 Jun 27 '18 at 18:20

8 Answers8

43

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 = () => {
        ...
    }

}
dctucker
  • 679
  • 5
  • 12
  • 1
    This does not currently seem to work (as far as I can tell). The new way to do this as shown in the documentation is: `this.focusListener = this.props.navigation.addListener('willFocus', payload => {this.load()});` (taken from https://reactnavigation.org/docs/en/navigation-prop.html#addlistener-subscribe-to-updates-to-navigation-lifecycle ) – Wige Jul 28 '19 at 07:38
  • Works for me with Expo 33.0.7. Simple and effective, thanks – James Cushing Sep 21 '19 at 14:59
  • Wow I can't believe it works as easily as this, made my day! @dctucker How did you figure it out though? I mean, where did you find out about 'willFocus'? – instanceof Nov 11 '19 at 12:45
  • 4
    This answers gives a solution that works with react-navigation, not react-native-navigation as asked by OP :/ See Stephen Liu and my answers bellow for RNN solutions. – samzmann Nov 19 '19 at 08:41
  • saved my day! react navigation is tough... But you seem to have cracked it! – Bikram Nath May 30 '20 at 08:25
6

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
João Paulo
  • 71
  • 1
  • 2
5

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()
    )
}
Bruno Reis
  • 326
  • 2
  • 7
4

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.

Stephen Liu
  • 141
  • 4
2

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.

David Schumann
  • 13,380
  • 9
  • 75
  • 96
Hendy Irawan
  • 20,498
  • 11
  • 103
  • 114
  • This is for react-navigation. The question was for react-native-navigation – sytolk Sep 05 '17 at 04:14
  • Aha! I wasn't even aware of that distinction! Since I was also using React Native I thought that was the same. Sorry about it. Naming is the hardest problem in computer science, indeed. :) – Hendy Irawan Sep 05 '17 at 12:57
2

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

1

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!
  })

}
samzmann
  • 2,286
  • 3
  • 20
  • 47
  • Sadly does not work `Uncaught TypeError: can't access property "componentDidAppear", _this is undefined` – kwoxer Aug 11 '20 at 05:46
-4

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

  • 2
    Thanks but this does not meet my needs. I would like an event handler to be triggered every time the user navigates back to a screen. – Daryn Oct 17 '16 at 12:58
  • You can try doing it inside render method instead then – Peter Gelderbloem Oct 17 '16 at 13:00
  • 1
    The render method does not get called when I click on the tab navigation. – Daryn Oct 17 '16 at 13:09
  • The user can't see it, if they have clicked onto a different tab. Tabs are shown in this example - https://github.com/wix/react-native-navigation/blob/master/example/src/app.js – Daryn Oct 17 '16 at 13:18
  • 2
    If a user clicks on on tab, then back again, then I want to reload data on the screen. – Daryn Oct 17 '16 at 13:19
  • Need to get this too. Something like onFocus/onAppear/onShown or similar... – Hendy Irawan Aug 28 '17 at 11:54