software-mansion / react-native-screens

Native navigation primitives for your React Native app.
MIT License
2.9k stars 496 forks source link

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

Open BadLice opened 1 month ago

BadLice commented 1 month ago

Description

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>
  );
};

here it is an example of the bug behaviour:

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

Steps to reproduce

  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

Snack or a link to a repository

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

Screens version

3.31.1

React Native version

0.74.1

Platforms

iOS

JavaScript runtime

Hermes

Workflow

React Native (without Expo)

Architecture

Paper (Old Architecture)

Build type

Debug mode

Device

Real device

Device model

iPhone 12

Acknowledgements

Yes

kkafar commented 1 month ago

Hey @BadLice, thanks for reporting the issue.

Would you mind to check whether

haven't fixed the issue?

As this PR landed already on main you can test it by installing this library from source code by putting following in your package.json:

"react-native-screens": "software-mansion/react-native-screens#main"
BadLice commented 1 month ago

hi @kkafar, thank you for your response.

unfortunately, this PR didn't fix my issue;

i created a new branch in my reproducer app resolving this library from main branch, so you can see the issue still persists: https://github.com/BadLice/modal-freeze-reproducer/tree/react-native-screens-main

hirbod commented 3 weeks ago

@BadLice have you tried to pass presentationStyle="overFullScreen" to Modal? This helps sometimes. Can you try my PR? I think this might fix the issue https://github.com/software-mansion/react-native-screens/pull/2175

BadLice commented 4 days ago

hi @hirbod, thanks for your response.

Your PR fixed my issue!

instead, passing presentationStyle="overFullScreen" did not help.

Thank you!

hirbod commented 3 days ago

@kkafar I'll prepare a reproducer for you now, but as you can see, my PR solves even more issues :)