wheatandcatの開発ブログ

React Nativeで開発しているペペロミア & memoirの技術系記事を投稿してます

React Navigation v5 → v6への移行

React Navigation v6がリリースされたので移行してみた

reactnavigation.org

PR

github.com

実装

v5→v6の移行は、以下の記事を読んで進めた

https://reactnavigation.org/docs/upgrading-from-5.x/

package.jsonを更新

package.json

    "@react-navigation/native": "^6.0.2",
    "@react-navigation/stack": "^6.0.7",

起動はそのまま行えたが、以下の箇所でwarning、typeエラーが発生していたので修正

useNavigation、createStackNavigatorにtypeの指定が必要になった

useNavigationcreateStackNavigatorにtypeを指定しないで、navigateを使用するとtypeチェックでエラーになるようになった

src/components/organisms/SettingModal/SettingModal.tsx:32:25 - error TS2345: Argument of type 'string' is not assignable to parameter of type '{ key: string; params?: undefined; merge?: boolean | undefined; } | { name: never; key?: string | undefined; params: never; merge?: boolean | undefined; }'.

32     navigation.navigate('SettingLicence');

以下みたいに、typeを設定すればエラーにならなくなります

import { RootStackParamList } from 'lib/navigation';
import { RouteProp } from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack';
import { useNavigation } from '@react-navigation/native';

type ScreenNavigationProp = StackNavigationProp<
  RootStackParamList,
  'Home'
>;
type ScreenRouteProp = RouteProp<RootStackParamList, 'Home'>;

export type Props = {
  navigation: ScreenNavigationProp;
  route: ScreenRouteProp;
};

const SettingModal: React.FC<Props> = (props) => {
  const navigation = useNavigation<HomeScreenNavigationProp>();

Stack.Groupを使用する

v5までだと、各画面のまとまり毎にStack.NavigatorでWrapするような作りでしたが、v6からその作りだと以下みたいなwarningが発生する

Found screens with the same name nested inside one another. Check:

Home, Home > Home

This can cause confusing behavior during navigation. Consider using unique names for each screen instead.
at node_modules/@sentry/utils/dist/instrument.js:111:20 in <anonymous>
at src/Router.tsx:5:0 in <global>
at node_modules/@react-navigation/core/src/useOnAction.tsx:135:9 in React.useCallback$argument_0

こちらの対処方法は、Stack.Groupを使用すればOK。 修正的には以下のようになる。

■ 修正前

      <Stack.Navigator initialRouteName="Home" mode="modal">
        <Stack.Screen
          name="Home"
          component={Home}
          options={{ headerShown: false }}
        />
        <Stack.Screen
          name="MyPage"
          component={MyPage}
          options={{ headerShown: false }}
        />
      </Stack.Navigator>

で↑のMyPageのコンポーネント内で更に以下を実装していた。

src/components/pages/MyPage/index.tsx

const Stack = createStackNavigator<RootStackParamList>();

const RootStack = () => {
  return (
    <Stack.Navigator initialRouteName="MyPage">
      <Stack.Screen
        name="MyPage"
        component={MyPage}
        options={MyPageScreenOption('マイページ')}
      />
      <Stack.Screen
        name="Login"
        component={Login}
        options={MyPageScreenOption('サインイン')}
      />
      <Stack.Screen
        name="UpdateProfile"
        component={UpdateProfile}
        options={MyPageScreenOption('プロフィール編集')}
      />
      <Stack.Screen
        name="SettingAddShareUser"
        component={SettingAddShareUser}
        options={MyPageScreenOption('共有メンバー追加')}
      />
      <Stack.Screen
        name="SettingRelationshipRequests"
        component={SettingRelationshipRequests}
        options={MyPageScreenOption('共有メンバー申請')}
      />
      <Stack.Screen
        name="SettingAcceptedRelationship"
        component={SettingAcceptedRelationship}
        options={MyPageScreenOption('')}
      />
    </Stack.Navigator>
  );
};
export default RootStack;

このコードが修正後は以下の通りになった。

■ 修正後

      <Stack.Navigator initialRouteName="Home">
        <Stack.Screen
          name="Home"
          component={Home}
          options={HomeScreenOption()}
        />
        <Stack.Group screenOptions={{ presentation: 'modal' }}>
          <Stack.Group>
            <Stack.Screen
              name="MyPage"
              component={MyPage}
              options={MyPageScreenOption('マイページ')}
            />
            <Stack.Screen
              name="Login"
              component={Login}
              options={MyPageScreenOption('サインイン')}
            />
            <Stack.Screen
              name="UpdateProfile"
              component={UpdateProfile}
              options={MyPageScreenOption('プロフィール編集')}
            />
            <Stack.Screen
              name="SettingAddShareUser"
              component={SettingAddShareUser}
              options={MyPageScreenOption('共有メンバー追加')}
            />
            <Stack.Screen
              name="SettingRelationshipRequests"
              component={SettingRelationshipRequests}
              options={MyPageScreenOption('共有メンバー申請')}
            />
            <Stack.Screen
              name="SettingAcceptedRelationship"
              component={SettingAcceptedRelationship}
              options={MyPageScreenOption('')}
            />
          </Stack.Group>
    </Stack.Navigator>

ナビゲーションの宣言内でStack.GroupでWrapしてあげればOKです。 ナビゲーションの書き方がシンプルになった。

modalの指定はscreenOptionsを使用する

v5ではmodalの宣言はStack.Navigatorのmodeをmodalに指定する感じでしたが、

<Stack.Navigator initialRouteName="Home" mode="modal">

v6からはscreenOptionsのpresentationにmodal'を指定する形式になった。

<Stack.Group screenOptions={{ presentation: 'modal' }}>