web-dev-qa-db-fra.com

React Navigation 5, bloquer la navigation arrière après la connexion

J'utilise React Navigation 5 dans un projet, et j'ai du mal à empêcher un utilisateur de revenir en arrière après un certain point.

L'application utilise une structure de navigation imbriquée similaire à celle-ci:

ROOT (STACK)
|-- LoginScreens (STACK - options={{ gestureEnabled: false }} )
|   |-- Login (SCREEN) -> when successful navigate to "Home"
|   +-- Register (SCREEN) -> after registration, navigate to "Login"
|
+-- Home (TABS - options={{ gestureEnabled: false }} )
    |-- BlahBlah (SCREEN)
    |-- MyProfile (SCREEN)
    +-- Dashboard (TABS)
        |-- AllTasks (SCREEN)
        +-- SomethingElse (SCREEN)

Après une connexion réussie, l'utilisateur est envoyé à l'écran Home et ne devrait pas pouvoir revenir à l'écran LoginScreens.

J'ai essayé d'utiliser la méthode du cycle de vie componentDidMount sur Home, ainsi que le crochet useFocusEffect, avec ce qui suit:

  • Placer un rappel vers React Native's BackHandler, renvoyer true à partir des travaux du gestionnaire (true signifie que l'action de retour a été gérée, aucun autre gestionnaire de retour ne sera appelé), mais il sera également bloquer toute navigation arrière dans les écrans dans Home (par exemple, je ne peux pas revenir du tableau de bord à mon profil).
  • Utilisation de navigation.reset({ index: 1, routes: [{ name: "Home" }] }). Sans index: 1, La navigation revient simplement à la route initiale de ROOT (dans ce cas, LoginScreens). Avec index: 1, Une erreur Maximum update depth exceeded Est générée.
  • Au lieu de naviguer directement vers Home, j'ai essayé d'utiliser une navigation.reset() (note: pas de paramètres, efface tout l'historique de navigation), et après cela, accédez à l'écran Home. Cela n'atteint pas l'effet souhaité puisque la route actuelle (la route initiale de ROOT, dans ce cas: LoginScreens) est toujours poussée sur l'historique de navigation avant de naviguer vers Home.
  • En combinant la navigation et la réinitialisation des appels de différentes manières, je n'ai réussi qu'à mettre JS en colère et à me lancer des erreurs et des exceptions.

Aaaaand ... J'ai manqué d'idées. Est-ce que quelqu'un a des suggestions ?

3
cristian

Eh bien, je dois admettre que ce n'était pas facile de trouver la syntaxe de la nouvelle méthode de réinitialisation pour la v5, man ... Les documents ReactNavigation ont vraiment besoin d'une fonctionnalité de recherche sur le site.

Quoi qu'il en soit, méthode de réinitialisation peut être utilisé et a parfaitement fonctionné pour moi.

Cela ressemble à quelque chose comme:

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

navigation.dispatch(
  CommonActions.reset({
    index: 0,
    routes: [
      {
        name: 'Home',
        params: { user: 'jane' },
      },
    ],
  })
);

J'ai créé une fonction d'assistance que j'utilise à plusieurs endroits dans mon application, qui ressemble à:

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

export const resetStackAndNavigate = (navigation, path) => {
  navigation.dispatch(CommonActions.reset({ index: 0, routes: [{ name: path }] }));
};
1

Je l'ai fait de cette façon pour react-navigation v5:

J'ai créé un CustomDrawerContent-Component pour pouvoir gérer chaque pression sur un élément:

(Note: Ignorez les propriétés header et footer, ce n'est qu'un ajustement pour mon tiroir.)

...
import {
  DrawerContentScrollView,
  DrawerItem,
} from '@react-navigation/drawer';
...

function CustomDrawerContent(props) {
  const {
    state: {routes, index},
    descriptors,
    navigation,
    header,
    footer,
  } = props;
  return (
    <>
      {header}
      <DrawerContentScrollView {...props}>
        {routes.map((route, i) => {
          const focused = i === index;
          const {title, drawerLabel, drawerIcon} = descriptors[
            route.key
          ].options;

          return (
            <DrawerItem
              key={route.key}
              label={
                drawerLabel !== undefined
                  ? drawerLabel
                  : title !== undefined
                  ? title
                  : route.name
              }
              icon={drawerIcon}
              focused={focused}
              onPress={() => {
                navigation.dispatch(
                  CommonActions.reset({index: i, routes: [{name: route.name}]}),
                  // NOTICE: Removes the routes.<name>.state of the Stack to discard
                  // navigation-Position if coming back to it via Drawer-Menu.
                  // If this Stack-State in seeded later on, you can adjust it here needed
                );
              }}
            />
          );
        })}
      </DrawerContentScrollView>
      {footer}
    </>
  );
}

function MainDrawer(props) {
  const {
    screen,
    screen: {initialRoute},
    navigatorProps,
    header,
    footer,
    hideDrawerItems,
  } = props;
  return (
    <Navigator
      initialRouteName={initialRoute}
      {...navigatorProps}
      drawerContent={(drawerProps) => (
        <CustomDrawerContent {...drawerProps} header={header} footer={footer} />
      )}>
      {createMenuEntries(screen, hideDrawerItems)} // that's  only an custom implementation of mine to create <Screen>-Entries. Feel free to replace it with your own
    </Navigator>
  );
}

export default MainDrawer;

La magie au moins est là:

{routes.map((route, i) => {
...
onPress => navigation.dispatch => CommonActions.reset({index: ⇒⇒ i ⇐⇐

Pendant que nous mappons sur chaque route, nous utilisons l'index actuel et le nom de la route (de l'élément de tiroir lui-même) pour réinitialiser son état de route, si nous l'avons tapé.

Cela fonctionne parfaitement pour mes besoins, car même si vous êtes dans News ⇒ News Detail"et ouvrez le tiroir et cliquez à nouveau sur News, vous êtes redirigé vers le premier écran de votre News-Stack.

enter image description here

0
suther