react-navigation / react-navigation

Routing and navigation for your React Native apps
https://reactnavigation.org
23.33k stars 4.98k forks source link

App randomly freezes when react-native Modal is shown while preventing remove screen with usePreventRemove on iOS #11981

Open BadLice opened 3 weeks ago

BadLice commented 3 weeks ago

Current behavior

I have a NativeStackNavigator with 2 screens; on the first screen i have a button to navigate to the second screen; on the second screen, i am using the usePreventRemove hook to show a confirmation modal to the user when i tries to navigate to the previous screen;

import React, {useCallback, useState} from 'react';
import {
  NavigationAction,
  useIsFocused,
  useNavigation,
} from '@react-navigation/native';
import usePreventRemove from '@react-navigation/core/src/usePreventRemove';
import {NativeStackNavigationProp} from '@react-navigation/native-stack';
import {Modal, Text, View, StyleSheet} from 'react-native';
import {Button} from './Button.tsx';
import {Routes} from './StackNavigator.tsx';

export const ExitConfirmModal = () => {
  const navigation =
    useNavigation<NativeStackNavigationProp<Routes, keyof Routes>>();
  const [pendingAction, setPendingAction] = useState<NavigationAction>();
  const isFocused = useIsFocused();

  const handlePressCancel = useCallback(() => {
    setPendingAction(undefined);
  }, []);

  const handlePressConfirm = useCallback(
    (action: NavigationAction) => {
      setPendingAction(undefined);
      navigation.dispatch(action);
    },
    [navigation],
  );

  usePreventRemove(isFocused && !pendingAction, event => {
    setPendingAction(event.data.action);
  });

  if (!pendingAction) {
    return null;
  }

  return (
    <Modal
      animationType="none"
      hardwareAccelerated
      transparent
      visible
      onRequestClose={handlePressCancel}>
       <View>
        <Button onPress={() => handlePressConfirm(pendingAction)}>
          <Text>Go back</Text>
        </Button>
        <Button onPress={handlePressCancel}>
          <Text>Cancel</Text>
        </Button>
      </View>
    </Modal>
  );
};

the bug occurs when:

  1. open the app
  2. press "go to second" button to navigate to second screen
  3. press the back button to go back to previous screen
  4. when the modal open, press cancel
  5. repeat step 3
  6. sometimes the app freezes when the modal appears.

the freeze occurs randomly, so repeating steps from 3 to 5 multiple times is sometimes needed. in order to enhance the chances of the bug to occur, it is useful to run the app on a physical device (iPhone) and repeat steps from 3 to 5 multiple times rapidly. I was able to reproduce the bug only on iOS

here it is an example of the bug behaviour:

https://github.com/react-navigation/react-navigation/assets/37150312/48ade50f-a6d9-4a3b-bf69-abdcb9736c5c

Expected behavior

the app does not freeze when the modal is shown

Reproduction

https://github.com/BadLice/modal-freeze-reproducer

Platform

Packages

Environment

package version
@react-navigation/native 6.1.17
@react-navigation/drawer 6.6.15
@react-navigation/stack 6.3.29
@react-navigation/native-stack 6.9.26
react-native-safe-area-context 4.10.1
react-native-screens 3.31.1
react-native-gesture-handler 2.16.2
react-native-reanimated 3.10.1
react-native 0.74.1
node 21.7.1
yarn 3.6.4
github-actions[bot] commented 3 weeks ago

Hey! Thanks for opening the issue. Seems that this issue is related to react-native-screens library which is a dependency of React Navigation. Can you also post your issue in this repo so that it's notified to the maintainers of that library? This will help us fix the issue faster since it's upto the maintainers of that library to investigate it.