Reputation: 3778
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
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
Upvotes: 2
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
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
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
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
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
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
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