Prevent default back navigation when using expo-router
Asked Answered
F

3

10

I'm currently using expo and expo-router for the first time and have a question about the default back navigation. What I mean by that is for example swiping from the left edge to the middle of the screen on Google Pixel. This will trigger a back navigation on my Stack navigator.

In my previous project with React Navtive CLI and React Navigation I could prevent that by adding a event listener to the "beforeRemove" event of the navigation prop.

How can I achieve this with expo-router? I followed their instruction to buid a CustomStack navigator and was hoping I can access "navigation" that way. My code looks basically like this:

CustomStack.tsx

import { createStackNavigator, StackNavigationOptions } from "@react-navigation/stack";
import { withLayoutContext } from "expo-router";

const { Navigator } = createStackNavigator();

export const CustomStack = withLayoutContext<StackNavigationOptions, typeof Navigator>(Navigator);

_layout.tsx

import { Stack } from "expo-router";
import { CustomStack } from "./CustomStack";

export default function RootLayout() {
return (
    <CustomStack>
        <Stack.Screen name="Home" />
        <Stack.Screen name="About" />
// ...
    </CustomStack>
  );
}

About.tsx

import type { StackScreenProps } from "@react-navigation/stack";
import { useEffect } from 'react';

export default function About({ navigation }: StackScreenProps<{}>) {

useEffect(() => {
   navigation.addListener('beforeRemove', (e) => {
        e.preventDefault();
        Alert.alert(
          'Changes might be lost',
          'Really want to go back?',
          [
            { text: "Cancel", style: 'cancel', onPress: () => { } },
            {
              text: "Yes",
              style: 'destructive',
              onPress: () => navigation.dispatch(e.data.action),
            },
          ]
        );
      };
    });
  }, [navigation]);

In my RN CLI app without TypeScript this useEffect in About.jsx works. Did I maybe mess up the types? I'm using TypeScript with RN for the first time and followed this documentation.

I really like the approach of expo-router that defines my navigation routes based on my files as well as the possibility to add layout files to my folders. So it would be amazing if someone could explain how to prevent the default back navigation while still being able to use expo-router.

Thanks for your help,
Thomas

I tried to use the navigation property to attach a event and prevent the default back navigation. I created a CustomStack navigator and imported all of the types according to the documentations. I was expecting that I can access the navigation property in my functional component and add event listener to it. The property however is undefined.

Feer answered 23/3, 2023 at 17:39 Comment(0)
T
15

There is no need to use custom stack for this. The solution is way simpler because you only need to useNavigation from expo and not the react-native one like you did.

So you can use expo-router stack and update your About component like this :

    import { useNavigation } from 'expo-router';
    import { useEffect } from 'react';
    
    export default function About() {

        // Navigation
        const navigation = useNavigation();

        // Effect
        useEffect(() => { 
            navigation.addListener('beforeRemove', (e) => {
                e.preventDefault();
                console.log('onback');
                // Do your stuff here
                navigation.dispatch(e.data.action);
            });
        }, []);

    }

Hope this would be helpful, the expo-router documentation is not complete for now.

Keep in mind that you would manage the listener to prevent unexpected calls of the listener with removeListener for example.

Triolet answered 31/3, 2023 at 11:4 Comment(2)
Thanks a lot. I can't believe I missed that when checking the documentation :-) Maybe a quick general advice for future readers: The addListener function returns a cleanup callback which should be called in the return statement of useEffect. It's not really relevant for the short code snippets in this question, but if you have more parameters in your dependy array it is necessary.Maltz
Note here. This will not work if you are using the back button on iOS because this will trigger native navigation. See limitations here: reactnavigation.org/docs/preventing-going-backRyter
O
0
import { useNavigation } from "expo-router";


 useEffect(() => {
        const navigation = useNavigation();
        navigation.addListener("beforeRemove", (e) => {
       // Prevent default behavior of leaving the screen
       if (e.data.action.type === "GO_BACK") {
        e.preventDefault();
      }
    });
  }, []);

You can log e.data.action to see which type you want to prevent. In this situation type === "GO_BACK" will be prevent

Outrageous answered 19/7 at 2:59 Comment(0)
D
-1

The most simplest way to handle back navigation is using BackHandler API by react-native

import {BackHandler} from 'react-native';

   useEffect(() => {

     const backAction = () => {
       Alert.alert('Hold on!', 'Are you sure you want to go back?', [
       {
          text: 'Cancel',
          onPress: () => null,
          style: 'cancel',
        },
        {text: 'YES', onPress: () => BackHandler.exitApp()},
        ]);
     return true;
    };

const backHandler = BackHandler.addEventListener(
  'hardwareBackPress',
  backAction,
);

return () => backHandler.remove();

}, []);

Donadonadee answered 12/7 at 12:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.