web-dev-qa-db-ja.com

Reactナビゲーション5、ログイン後に戻るナビゲーションをブロック

プロジェクトで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画面に戻ることができなくなります。

componentDidMountHomeライフサイクルメソッドとuseFocusEffectフックを次のように使用しようとしました:

  • React NativeのBackHandlerへのコールバックを配置すると、ハンドラーからtrueが返されます(trueは、バックアクションが処理され、それ以上バックハンドラーが呼び出されないことを意味します)。 Homeの画面内のバックナビゲーションをすべてブロックします(たとえば、ダッシュボードからMyProfileに戻ることができません)。
  • navigation.reset({ index: 1, routes: [{ name: "Home" }] })を使用します。 _index: 1_がない場合、ナビゲーションはROOTのinitialRoute(この場合はLoginScreens)に戻ります。 _index: 1_を使用すると、_Maximum update depth exceeded_エラーがスローされます。
  • Homeに直接移動する代わりに、navigation.reset()を使用してみました(注:paramsなし、ナビゲーション履歴全体をクリアします)。その後、Home画面に移動します。現在のルート(ROOTのinitialRoute、この場合はLoginScreens)がHomeにナビゲートする前にナビゲーション履歴にプッシュされるため、これは望ましい効果を達成しません。
  • ナビゲーションとリセットの呼び出しをさまざまな方法で組み合わせると、JSが怒ってエラーと例外をスローすることができました。

Aaaaand ...私はアイデアを使い果たしました。誰か提案はありますか?

3
cristian

まあ、私は認めざるを得ません。v5の新しいリセットメソッドの構文を見つけるのは簡単ではありませんでした。ReactNavigationドキュメントには、サイト内検索機能が本当に必要です。

とにかく、 reset method を使用でき、私にとっては完全に機能しました。

それは次のようになります:

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

私は反応ナビゲーション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;

少なくとも魔法はここにあります:

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

各ルートをマッピングする際、現在のインデックスと(ドロワーアイテム自体の)ルート名を使用して、ルート状態をリセットします(タップした場合)。

News ⇒ News Detail"にいて、ドロワーを開いてNewsをもう一度クリックしても、News-Stackの最初の画面にパイプされるので、これは私の目的には完全にうまく機能します。

enter image description here

0
suther