Daryn
Daryn

Reputation: 3778

How to trigger an event when a component is shown when using react-native-navigation?

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

Upvotes: 42

Views: 65668

Answers (8)

hafid id-baha
hafid id-baha

Reputation: 21

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

Upvotes: 2

João Paulo
João Paulo

Reputation: 81

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

Upvotes: 7

samzmann
samzmann

Reputation: 2566

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

}

Upvotes: 1

Stephen Liu
Stephen Liu

Reputation: 141

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.

Upvotes: 4

dctucker
dctucker

Reputation: 688

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

}

Upvotes: 43

Bruno Reis
Bruno Reis

Reputation: 326

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

Upvotes: 5

Hendy Irawan
Hendy Irawan

Reputation: 21404

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.

Upvotes: 2

Peter Gelderbloem
Peter Gelderbloem

Reputation: 512

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

Upvotes: -4

Related Questions