Reputation: 1646
I'm developing a Mobile Application using React Native. There, I'm passing something when navigating to the second page.
So, assume my first screen has something like this.
export default function firstScreen(props) {
return(
//data is a custom object variable that may be changed.
<Button onPress={() => props.navigation.navigate('SecondScreen', { data: data } )} />
)
}
On my second screen, assume there is something like this...
export default function secondScreen(props) {
const { data } = props.navigation.state.params;
useEffect(() => {
console.log(data);
}, [data])
return(
//content
)
}
My problem is, when data
is changed from the first screen, the second screen does not listen to that change and print the content of the data
variable on the console.
So, I want to know, how to listen to the navigation state changes in React Native.
Upvotes: 6
Views: 15123
Reputation: 2761
You might also look into using an event listener on a navigation ref that is attached to your navigation container. See docs here: https://reactnavigation.org/docs/navigation-events/
The navigationRef can be exported and accessed most anywhere. If you wanted to check navigation state from a parent of the navigation container, it could look something like this:
import { useEffect } from 'react'
import {
createNavigationContainerRef,
EventListenerCallback,
NavigationContainerEventMap,
NavigationContainer,
} from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'
type StackParamList = {
Screen1: undefined
Screen2: undefined
}
const Screen1 = () => <></>
const Screen2 = () => <></>
const navigationRef = createNavigationContainerRef<StackParamList>()
const Stack = createNativeStackNavigator<StackParamList>()
const Navigation = () => (
<NavigationContainer ref={navigationRef}>
<Stack.Navigator>
<Stack.Screen name="Screen1" component={Screen1} />
<Stack.Screen name="Screen2" component={Screen2} />
</Stack.Navigator>
</NavigationContainer>
)
const App = () => {
useEffect(() => {
const handleStateChange: EventListenerCallback<
NavigationContainerEventMap,
'state'
> = (event) => {
const state = event.data.state
// do stuff with state
console.log(state)
}
// Add listener on mount
navigationRef.addListener('state', handleStateChange)
// Remove listener on unmount
return () => navigationRef.removeListener('state', handleStateChange)
}, [navigationRef])
return <Navigation />
}
export default App
Listening for this event is even easier within the navigation container; you can use the useNavigationState hook inside components per Guruparan's answer if you want access to component state, or you can use the onStateChange callback built into navigation containers:
<NavigationContainer
onStateChange={(state) => {
// do stuff with state
console.log(state)
}}
>
<Stack.Navigator>
<Stack.Screen name="Screen1" component={Screen1} />
<Stack.Screen name="Screen2" component={Screen2} />
</Stack.Navigator>
</NavigationContainer>
Upvotes: 2
Reputation: 807
Following is how I solved this problem with hooks in React Navigation V5:
import { useRoute } from '@react-navigation/native';
const route = useRoute();
useEffect(() => { //This will run whenever params change
const {params = {}} = route;
//your logic here
}, [route]);
Upvotes: 6
Reputation: 16354
You can use the hook 'useNavigationState' to get the navigation state from the second screen.
Then again you will have the problem of actually updating it from the first screen. When you pass a parameter its part of the navigation state and the changes you to do the object you have wont be reflected there.
One way to update navigation state would be to dispatch an action, but that will be a problem because you need to find the key of the screen you are going to update. the dispatch would be something like this
navigation.dispatch({ ...CommonActions.setParams({ data: {} }), source: route.key, });
As you have the need to get the updates of your object to another screen you should consider something like react context or if you already have redux in your app you can utilize that if the need is there.
Easiest way would be the react context which you can update from one screen and receive from another.
The version of using context would be like below. Here a simple stack navigator is used and the context is updated from home screen and the value can be read from settings screen. You can use the same way to update the data variable and get the data from the other screen. Here we dont use the navigation state but we maintain our own context for the variable.
const Stack = createStackNavigator();
const CountContext = React.createContext({ count: 1, setCount: () => {} });
function HomeScreen({ navigation, route }) {
const { count, setCount } = React.useContext(CountContext);
React.useEffect(() => {
const interval = setInterval(() => {
setCount(count => count + 1);
}, 1000);
return () => {
clearInterval(interval);
};
}, []);
return (
<View>
<Text>Test{count}</Text>
<Button
title="Go to settings"
onPress={() => navigation.navigate('Settings')}
/>
</View>
);
}
function SettingsScreen({ navigation, route }) {
const { count, setCount } = React.useContext(CountContext);
return (
<View>
<Text>Settings! {count}</Text>
<Button title="Go back" onPress={() => navigation.goBack()} />
</View>
);
}
export default function App() {
const [count, setCount] = React.useState(0);
const value = { count, setCount };
return (
<CountContext.Provider value={value}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Navigator>
</NavigationContainer>
</CountContext.Provider>
);
}
You can check out this snack https://snack.expo.io/@guruparan/stateparamschange
Upvotes: 2