如何防止使用 React Navigation 和带有自定义模式的 'beforeRemove' eventListener 返回 React Native?

How to prevent going back in React Native using React Navigation and 'beforeRemove' eventListener with custom modal?

我想实现的很简单。我希望用户被迫确认退出名为 'checkout'.

的选项卡导航器

我在 React Navigation 文档上阅读了关于 preventing going back 关于 'beforeRemove' 事件的内容,它看起来很简洁并且使用正确。

问题在于,在他们的示例中,他们在事件侦听器中调用警报,而我想显示带有是和否按钮的自定义模式。

这是 React Navigations 示例代码:

function EditText({ navigation }) {
  const [text, setText] = React.useState('');
  const hasUnsavedChanges = Boolean(text);

  React.useEffect(
    () =>
      navigation.addListener('beforeRemove', (e) => {
        if (!hasUnsavedChanges) {
          // If we don't have unsaved changes, then we don't need to do anything
          return;
        }

        // Prevent default behavior of leaving the screen
        e.preventDefault();

        // Prompt the user before leaving the screen
        Alert.alert(
          'Discard changes?',
          'You have unsaved changes. Are you sure to discard them and leave the screen?',
          [
            { text: "Don't leave", style: 'cancel', onPress: () => {} },
            {
              text: 'Discard',
              style: 'destructive',
              // If the user confirmed, then we dispatch the action we blocked earlier
              // This will continue the action that had triggered the removal of the screen
              onPress: () => navigation.dispatch(e.data.action),
            },
          ]
        );
      }),
    [navigation, hasUnsavedChanges]
  );

  return (
    <TextInput
      value={text}
      placeholder="Type something…"
      onChangeText={setText}
    />
  );
}

这是我试过的代码:

useEffect(() => {
    navigation.addListener('beforeRemove', e => {
      if (userConfirmedExit) {
        navigation.dispatch(e.data.action);
      } else {
        e.preventDefault();
        setShowExitModal(true);
      }
    });
  }, [navigation, userConfirmedExit]);

  const handleConfirmExit = () => {
    setUserConfirmedExit(true);
    navigation.replace('ProfileTab');
  };

  const handleDeclineExit = () => setShowExitModal(false);

我必须在 eventListener 内部使用 navigation.dispatch(e.data.action) 但 handleConfirmExit 函数必须存在于它之外,我只是不知道如何使用 beforeRemove 侦听器和显示我可以从中退出选项卡的自定义模式。

当按下后退按钮时监听器正在触发并且模式显示但是当按下是时没有任何反应(即 运行 handleConfirmExit 函数)。

我已经尝试从 useEffect 中删除依赖项。唯一有效但仅在 Android 上起作用的是:

useEffect(() => {
    navigation.addListener('beforeRemove', e => {
      e.preventDefault();
      setShowExitModal(true);
    });
  }, [navigation]);

  const handleConfirmExit = () => {
    navigation.removeListener('beforeRemove', () => {});
    navigation.replace('ProfileTab');
  };

  const handleDeclineExit = () => setShowExitModal(false);

在 iOS 上,由于某种原因,模式停留在下一个屏幕上,我认为罪魁祸首是上一个示例中 'beforeRemove' 侦听器的错误实现。

谢谢!

使用 BackHandler ,你可以使用 navigation.goBack() 而不是 BackHandler.exitApp()

import { BackHandler} from "react-native";
const backAction = () => {
  Alert.alert("Discard changes?", "Are you sure you want to exit?", [
    {
      text: "NO",
      onPress: () => null,
      style: "cancel"
    },
    { text: "YES", onPress: () => BackHandler.exitApp() }
  ]);
  return true;
};

useEffect(() => {
  BackHandler.addEventListener("hardwareBackPress", backAction);
return () => {
  BackHandler.removeEventListener("hardwareBackPress", backAction);
}   
}, []);

我就是这样做的,效果很好,但我相信还有更好的解决方案。

const [showExitModal, setShowExitModal] = useState(false);
let exitEvent = useRef<
    EventArg<
      'beforeRemove',
      true,
      {
        action: Readonly<{
          type: string;
          payload?: object | undefined;
          source?: string | undefined;
          target?: string | undefined;
        }>;
      }
    >
  >();
  useEffect(() => {
    const unsubscribe = navigation.addListener('beforeRemove', e => {
      e.preventDefault();
      exitEvent.current = e;  
      setShowExitModal(true);    
    });
    return unsubscribe;
  }, [navigation]);

  const handleConfirmExit = () => {
    if (exitEvent.current) {
      navigation.dispatch(exitEvent.current.data.action);
    }
  };

在标记中:

 {showExitModal && (
        <CheckOutExitModal
          onYesPress={handleConfirmExit}
        />
      )}

我有一个简单的解决方案

 navigation.addListener('beforeRemove', (e) => {
      
        if (e.data.action.type !="GO_BACK") {
           //"GO_BACK" is emitted by hardware button

          navigation.dispatch(e.data.action);
        } else {
    //your code to prevent hardware back button goes here } // 

} )