Jude Fernandes
Jude Fernandes

Reputation: 7527

Get navigation props in an independent component React Native

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

Answers (3)

Fed C
Fed C

Reputation: 303

After a bit of research, the easiest way I found was to follow their official documentation:

  1. I created a 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);
}

  1. I imported it into App.js and created a reference to it in the return function:

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

  1. I called it inside the ScreenButton component

import 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

Pritish Vaidya
Pritish Vaidya

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

Stackia
Stackia

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

Related Questions