Реагировать Навигация 5, заблокировать обратную навигацию после входа в систему - PullRequest
2 голосов
/ 19 февраля 2020

Я использую React Navigation 5 в проекте, и у меня возникают проблемы при попытке запретить пользователю переходить назад после определенной точки.

Приложение использует вложенную структуру навигации, подобную этой:

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)

После успешного входа в систему пользователь отправляется на экран Home и не может вернуться к экрану LoginScreens.

Я пытался использовать * жизненный цикл componentDidMount на Home, а также ловушка useFocusEffect, со следующим:

  • Возврат обратного вызова в BackHandler React Native, возвращение true из обработчика ( Значение true означает, что обратное действие было обработано, дальнейшие обработчики возврата не будут вызываться), но оно также заблокирует любую обратную навигацию внутри экранов в Home (например, я не могу перейти обратно из Dashboard в MyProfile).
  • Использование navigation.reset({ index: 1, routes: [{ name: "Home" }] }). Без index: 1 навигация просто возвращается к исходному маршруту ROOT (в данном случае LoginScreens). С index: 1 выдается ошибка Maximum update depth exceeded.
  • Вместо перехода непосредственно к Home, я попытался использовать navigation.reset() (примечание: без параметров, очищает всю историю навигации), и после что перейти к экрану Home. Это не дает желаемого эффекта, так как текущий маршрут (ROOT initialRoute, в данном случае: LoginScreens) все еще помещен в историю навигации перед переходом к Home.
  • Объединение навигации и по-разному сбрасывать вызовы, мне только удалось разозлиться JS и выбросить в меня ошибки и исключения.

А-а-а-а ... У меня закончились идеи. У кого-нибудь есть предложения?

Ответы [ 3 ]

1 голос
/ 10 апреля 2020

Ну, я должен признать, что было нелегко найти синтаксис нового метода сброса для v5, человек ... Документы ReactNavigation действительно нуждаются в функциональности поиска по сайту.

В любом случае, сброс метод может быть использован и отлично работает для меня.

Это выглядит примерно так:

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

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

Я сделал вспомогательную функцию, которую я использую в нескольких местах в моем приложении, это выглядит так:

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

export const resetStackAndNavigate = (navigation, path) => {
  navigation.dispatch(CommonActions.reset({ index: 0, routes: [{ name: path }] }));
};
1 голос
/ 19 февраля 2020

Кажется, что документы React Navigation пытались охватить этот сценарий использования с этим руководством:

https://reactnavigation.org/docs/en/auth-flow.html

Пример здесь очень сложный, уже вводит состояние библиотеки управления, редукторы, перехватчики React и все остальное, что на самом деле не помогает. Тем не менее, краткое содержание этого руководства: Conditionally render routes.

Unlinke React Navigation 4 и предыдущих версий, в React Navigation 5 вы можете условно отображать маршруты. При этом вы эффективно исключаете любые возможности навигации по несуществующему маршруту. Ниже приведен очень короткий пример того, как вы можете сделать это с помощью простой переменной состояния. Имейте в виду, однако, что в этом примере учитывается только навигатор, отображающий только один маршрут за раз. Если у вас есть больше маршрутов, отличных от приведенных в этом примере, вам может потребоваться настроить параметры RootStack.Navigator (например, initialRouteName) или явно перейти к указанному маршруту c.

import React from "react";
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

import LoginNav from "./screens/LoginNav";
import HomeScreens from "./screens/HomeScreens";

const RootStack = createStackNavigator();

export default class MyApp extends React.Component {
    constructor(props){
        super(props);

        this.state = { isLoggedIn: false };
    }

    setIsLoggedIn = (isLoggedIn)=>{ this.setState({ isLoggedIn }); }

    render = () => {
        // Using an arrow function to allow to pass setIsLoggedIn to LoginNav
        // Pass setIsLoggedIn from the props of LoginNav to the screens it contains
        // then from the screens call this function with a true/false param
        const LoginScreens = (props)=> <LoginNav {...props} setIsLoggedIn={this.setIsLoggedIn} />

        return <NavigationContainer style={{ flex: 1 }}>
            <RootStack.Navigator>
                {(this.state.isLoggedIn === false)
                    // If not logged in, the user will be shown this route
                    ? <RootStack.Screen name="LoginScreens" component={LoginScreens} />
                    // When logged in, the user will be shown this route
                    : <RootStack.Screen name="Home" component={HomeScreens} />
                }
            </RootStack.Navigator>
        </NavigationContainer>;
    }
}

В этом примере вызовите (this.) props.setIsLoggedIn(true), чтобы отобразить маршрут Home, или вызовите с параметром false, чтобы вернуться к маршруту LoginScreens.

Надеюсь, это Пример легче понять, чем тот, что в документации.

0 голосов
/ 02 мая 2020

Я сделал это для реакции-навигации v5:

Я создал компонент CustomDrawerContent-Component, чтобы можно было обрабатывать каждое нажатие на элементе:

(Примечание: игнорировать свойства header и footer, это только корректировка для моего ящика.)

...
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;

Волхвы c по крайней мере здесь:

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

Пока мы наносим на карту каждый маршрут, мы используем текущий индекс и имя маршрута (самого элемента ящика), чтобы сбросить его состояние маршрута, если мы коснулись его.

Это прекрасно работает для моих целей, потому что даже если вы находитесь в News ⇒ News Detail", откройте ящик и снова щелкните по News, вы попадете на первый экран вашего News-Stack.

enter image description here

...