28

I have an application using React native where I am using react-navigation (5.2.9).

I built a Stack.Navigator where I've got my screens but I want the Footer component to be outside so it renders in all screens. The problem is, I can't navigate from the footer, which is what I need to do as the footer has a few buttons that should be changing the screen:

const Stack = createStackNavigator();

const App = () => {    
  return (
    <Provider store={store}>
      <NavigationContainer>
        <Header />
        <Stack.Navigator>
          <Stack.Screen
            name="Home"
            component={HomeScreen}
            options={{
            headerShown: false
          }}
          />
          <Stack.Screen
            name="Login"
            component={LoginScreen}
            options={{
            headerShown: false
          }}
          />
        </Stack.Navigator>
        <Footer />
      </NavigationContainer>
    </Provider>
  );
};

How do I pass the navigation prop to the footer component?

7 Answers 7

67
+200

Try this:

Create a new file named: RootNavigation.js

// RootNavigation.js

import * as React from 'react';

export const navigationRef = React.createRef();

export function navigate(name, params) {
  navigationRef.current?.navigate(name, params);
}
// file where you have the navigation

import {navigationRef} from './path/to/RootNavigation';

      <NavigationContainer ref={navigationRef}>
      .....
        <Footer />
      </NavigationContainer>

And Footer.js should be something like this:

// Footer.js

import React from 'react';
import {View, Button} from 'react-native';
import * as RootNavigation from './path/to/RootNavigation';

const Footer= () => {
  return (
    <View>
      <Button
        title={'Go to'}
        onPress={() =>
          RootNavigation.navigate('screenName ', {userName: 'Lucy'})
        }
      />
    </View>
  );
};

export default Footer;

For more info you can read the documentation. https://reactnavigation.org/docs/navigating-without-navigation-prop/

Sign up to request clarification or add additional context in comments.

6 Comments

Is this not literally what I answered, a day before?
Will this work if the file which I'm using the navigation in is not a component/screen ? As in, if it is not wrapped by NavigationContainer ? What I'm trying to achieve is to navigate to a particular screen when a notification is received. (I'm using react-native-push-notification for this.) But the configuration for push notifications is not to be added in any component; it should be in a separate file.
ref.current is always null. I log it in setInterval and it does not get initialized. Why?
Well, i typed navigationRef={navigationRef} instead of ref={navigationRef}. Two hours gone on that. oh god :/
@how can i use CommonActions.reset() this way?
|
25

The components outside of the Stack.Screen components do not receive the navigation prop. So in this case, you have to get the NavigationContainer using refs in your footer component, as follows:

  1. Create a ref with const myRef = React.createRef()
  2. pass it to the <NavigationContainer ref={myRef}>, and
  3. use that ref to navigate, myRef.current?.navigate(name, params).

It is all explained here. The docs here separate the creation of the ref in a new file, to allow you to import the ref without dependency loops/ issues. As the docs state, you can now call RootNavigation.navigate('ChatScreen', { userName: 'Lucy' }); in any js module, not just in a react component.

In your case though, you don't need the ref in separate file.

const Stack = createStackNavigator();
const navigationRef = React.createRef();

const App = () => {    
  return (
    <Provider store={store}>
      <NavigationContainer ref={navigationRef}>
        <Header />
        <Stack.Navigator>
          <Stack.Screen
            name="Home"
            component={HomeScreen}
            options={{
            headerShown: false
          }}
          />
          <Stack.Screen
            name="Login"
            component={LoginScreen}
            options={{
            headerShown: false
          }}
          />
        </Stack.Navigator>
        <Footer navigationRef={navigationRef}/>
      </NavigationContainer>
    </Provider>
  );
};

And in <Footer/> use navigationRef.current?.navigate(name, params)

1 Comment

I find this is much easier to follow that the approved answer
9

From the documentation. https://reactnavigation.org/docs/connecting-navigation-prop/

import * as React from 'react';
import { Button } from 'react-native';
import { useNavigation } from '@react-navigation/native';

function GoToButton({ screenName }) {
  const navigation = useNavigation();

  return (
    <Button
      title={`Go to ${screenName}`}
      onPress={() => navigation.navigate(screenName)}
    />
  );
}

2 Comments

Obviously I have tried what documentation says but this will only show the following error: Couldn't find a navigation.object, Is your component inside a screen in a navigator?
Using the same method, still getting the same error.
3

A simple trick to extract navigation out of screenOptions.

function NavWrapper() {
  const navigatorRef = useRef<any>();
  <Tab.Navigator
    screenOptions={({ navigation: nav }) => navigatorRef.current = nav}
  >
    <Tab.Screen name="screen1" />
  </Tab.Navigator>
}

Comments

2

I ended up building a component called screen that will just wrap the content of screen and render the header/footer based on props:

import React from 'react';
import { View } from 'native-base';
import style from './style';
import Footer from '../../footer';
import Header from '../../header';

const Screen = ({
    footer,
    header,
    children,
    navigation
}) => (
  <View style={style.screen}>
    { header && <Header navigation={navigation} />}
    { children }
    { footer && <Footer navigation={navigation} />}
  </View>
);

export default Screen;

And wrapping the screens of my apps like this:

<Screen header footer navigation={navigation}>
    ... screen content
</Screen>

I feel like it is the best way if I can't sort that problem out.

Comments

0

I solve this problem with a global state using React context API:

  // When enter in the HomeScreen:
  const { setGlobalNavigation, globalNavigation } = useGlobalContext();
  const navigation = useNavigation<RemindersNavigationProp>();

  useEffect(() => {
    if (setGlobalNavigation && !globalNavigation) setGlobalNavigation(navigation);
  }, [setGlobalNavigation, globalNavigation, navigation]);

  // When want to use:
  const { globalNavigation } = useGlobalContext();
  const handleNavigate = () => 
  globalNavigation.navigate("contactsStack", { screen: "newContact" });

Comments

0

Typescript-safe way:

Use CommonActions

import { createNavigationContainerRef  } from '@react-navigation/native';
import { CommonActions } from '@react-navigation/native';


export const navigationRef = createNavigationContainerRef()

export function navigate(routeName: string, params?: object) {
    if (navigationRef.isReady()) {
      navigationRef.dispatch(CommonActions.navigate(routeName, params));
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.