Reputation: 7527
My app.js
file looks like this
export default class App extends React.Component {
render() {
return (
<Root style={{
flex: 1
}}>
<FcmHandler/>
</Root>
)
}
}
The Root
component is where the entire app resides along with all the functionality, the FcmHandler
is where I handle functionality related to notifications etc. Within the FcmHandler
I have a method that gets a callback when a notification is clicked, inside this callback I need to navigate to a specific screen in the app based on the notification click.
The problem is using the current code above the FcmHandler
component never even gets initialized.
If I try something like this
export default class App extends React.Component {
render() {
return (
<View style={{
flex: 1
}}>
<Root/>
<FcmHandler/>
</View>
)
}
}
the FcmHandler
component gets called but I do not have any access to navigation props which reside inside the <Root/>
component.
The <Root/>
component consists of the following
const ArticleStack = StackNavigator(
{
...
}
);
const SettingsStack = StackNavigator({
...
});
export const Root = StackNavigator({
Articles: {
screen: ArticleStack
},
Settings: {
screen: SettingsStack
},
}, {
mode: 'modal',
headerMode: 'none'
});
The basic goal I am trying to achieve is, when a notification is click, irrespective of which screen the app is currently on I should be able to navigate to a particular screen. I do not want to write the navigation code in every screen component that I have, that seems redundant.
Upvotes: 3
Views: 6537
Reputation: 303
After a bit of research, the easiest way I found was to follow their official documentation:
RootNavigation.js
file in the ./misc
folder;import * as React from 'react';
export const navigationRef = React.createRef();
export function navigate(name, params) {
navigationRef.current?.navigate(name, params);
}
import React from 'react'
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { navigationRef } from './misc/rootNavigation'; <- navigationRef is imported
…
const Stack = createStackNavigator();
function App() {
return (
<Provider store={store}>
<NavigationContainer ref={navigationRef}> <— reference to navigationRef
<Stack.Navigator>
…
<Stack.Screen
name="Screen"
component={Screen}
options={{
title: “Hello”,
headerLeft: () => <ScreenButton/>
}} />
</Stack.Navigator>
</NavigationContainer>
</Provider>
);
}
export default App
ScreenButton
componentimport React, { Component } from 'react'
…
import * as RootNavigation from '../misc/rootNavigation'; <—- imported
class RoomButton extends Component {
constructor(props) {
super(props)
}
render() {
return (
<TouchableOpacity onPress={
() => {RootNavigation.navigate( 'RoomSelectorScreen' ) <—- called here
…
</TouchableOpacity>
)
}
}
Upvotes: 0
Reputation: 22209
For react-navigation
users, a really cool way is to create your own Navigation Service
You can initialize your Navigation Service module, during the time initializing your navigation store as mentioned in their docs
<AppNavigator navigation={addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.nav,
addListener,
})} />
// Just add another line to config the navigator object
NavigationService.configNavigator(dispatch) <== This is the important part
NavigationService.js
import { NavigationActions } from 'react-navigation'
let config = {}
const configNavigator = nav => {
config.navigator = nav
}
const reset = (routeName, params) => {
let action = NavigationActions.reset({
index: 0,
key: null,
actions: [
NavigationActions.navigate({
type: 'Navigation/NAVIGATE',
routeName,
params,
}),
],
})
config.navigator(action)
}
const navigate = (routeName, params) => {
let action = NavigationActions.navigate({
type: 'Navigation/NAVIGATE',
routeName,
params,
})
config.navigator(action)
}
const navigateDeep = actions => {
let action = actions.reduceRight(
(prevAction, action) =>
NavigationActions.navigate({
type: 'Navigation/NAVIGATE',
routeName: action.routeName,
params: action.params,
action: prevAction,
}),
undefined
)
config.navigator(action)
}
const goBack = () => {
if (config.navigator) {
let action = NavigationActions.back({})
config.navigator(action)
}
}
export default {
configNavigator,
navigateDeep,
navigate,
reset,
goBack,
}
Explanation :
The config
initializes the navigator's dispatch
object whenever your redux-navigation
gets initialzed, therefore you can dispatch any navigation action
, wrt the method's present in the Service Component.
Use
NavigationServices.navigate('ScreenName')
Update:
React Navigation now provides a HOC wrapper
withNavigation
, that passes the navigation prop into a wrapped component.
It's useful when you cannot pass the navigation prop into the component directly, or don't want to pass it in case of a deeply nested child.
Usage is well mentioned in their docs.
Upvotes: 1
Reputation: 2151
You can follow this official guide to create your navigation service. Then use the navigation service in FcmHandler
instead of navigation
prop. This way there is no need to put FcmHandler
as a child of the navigator.
If you are using redux or mobx, it's better to move your navigation state to the store for easier access. For redux, there is an official integration guide. For mobx, you can try this.
Upvotes: 2