CubicLouve

Spring_MTの技術ブログ

React Navigationを4系から5系にバージョンアップする

ReactNative は 0.63.2 まで更新してある状態で、React Navigationのバージョンを4系から5系にバージョンアップしました。

その時の対応をまとめます。

React Navigation 5のアップデート内容

下記にまとまっています。

reactnavigation.org

ここで取り上げられているテーマは下記の通り

  • React コンポーネントを使った設定
  • 新しいフック
  • コンポーネントからのオプションの更新
  • 新しいテーマAPI
  • TypeScript化(型を第一級に扱う)
  • Redux DevTools との統合
  • ネイティブなStack Navigator
  • ネイティブなMaterial top tab navigator

npm packageの更新

https://reactnavigation.org/docs/upgrading-from-4.x#package-names

使っていたライブラリについて、下記のように変更

react-navigation -> @react-navigation/native
react-navigation-stack -> @react-navigation/stack
react-navigation-tabs -> @react-navigation/bottom-tabs

@react-navigation/material-top-tabs@react-navigation/material-bottom-tabs は今のプロジェクトでは不要だったのでインストールしませんでした。

また、v4との互換性を保つための仕組みを提供してくれる @react-navigation/compat を使いました。

=> 最終的に使うのをやめました。

バージョンアップの方針

下記に4系からのアップデートについての方法が公式ページにあります。

Upgrading from 4.x | React Navigation

ここでもある通り、React Navigation 4 のメンテナンスは当面は継続して行い、React Native の最新バージョンにも追従すると書かれています。

いつまでメンテしてくれるかはなんか不安ですが、今すぐに開発が止まるということがなさそうなので、すぐに無理にバージョンアップしなくてもよさそうです。

今のプロジェクトではアップデートする時間を作れたのでアップデートを実施しました。

@react-navigation/compat を使う @react-navigation/compatは使わずにアップデートしました

React Navigation 5では4とはほぼ互換性がない状態です。

なので、いきなり全部対応しようとすると量が膨大になるので、まずはReact Navigationが用意した @react-navigation/compat を利用し、変更を最小限にしました。

今後の運用も考えて @react-navigation/compat は使わずにアップデートを行いました。

変更箇所

createStackNavigator createBottomTabNavigator を使ったNagvigator

v5での createXNavigator は 大幅に変更されており、React コンポーネントとしてnavigatorを設定するようになっています。

https://reactnavigation.org/docs/upgrading-from-4.x/#configuring-the-navigator

createStackNavigator | React Navigation

なので、ここは一旦 @react-navigation/compatcreateCompatNavigatorFactory を使って既存の呼び出しをラップしました。

Navigatorの数が少なかったのと今後の運用を加味して、v5の方式に移行しました。

v5ではScreenで柔軟にオプションを指定できるようになったため、同時にNavigatorの階層を見直し階層を減らしています。

下記に書いてあるとおり、今回のアップデートではかなり大きな方針変更がありました。

https://reactnavigation.org/docs/nesting-navigators/#navigating-to-a-screen-in-a-nested-navigator

v4以前では、遷移は静的に定義されていたのに対し、v5では動的に設定されるようになりました。

v4以前では、再帰的にScreenを探しにいきますが、v5ではそれがなくなりました。

v5では、現在のNavigator内で遷移できなければ、一階層上の親のNavigatorから探しに行きますが、それより上には行けないようです。

そのため、Navigatorの階層は2つくらいに抑えることが推奨されています。

When nesting multiple stack navigators, we recommend nesting at most 2 stack navigators, unless absolutely necessary.

Nesting navigators | React Navigation

createSwitchNavigator@react-navigation/compat のものを使う createSwitchNavigatorは利用をやめました

React Navigation 5 では、React コンポーネントを使うようになったため、条件分岐なども書けるようになりました。

そのため、NavigatorのScreen定義を、動的に定義して変更することができるので、Switch Navigatorが不要になります。

なので、createSwitchNavigator は廃止されているのですが、 今回修正範囲が大きかったので、一旦 @react-navigation/compat にある、createSwitchNavigator を使いました。

それに従い、 createStackNavigator に置き換えて対応しました。

NavigationStackScreenPropsStackNavigationProp に置き換える

v5では NavigationStackScreenPropsStackNavigationProp となりました。

v4

https://reactnavigation.org/docs/4.x/typescript/#type-checking-all-props-for-a-screen

v5

https://reactnavigation.org/docs/typescript/#type-checking-screens

これにより、Screenに対するparameterを型チェックできるようになります。

修正はこんな感じでした。

navigation.navigate を使った遷移にも型チェックが効くようになっています。

下記のような感じで全般的に書き直しました。

Screen名 + ParamList というtypeを用意しつつ、exportしています。

import { BarParamList } from 'src/screens/Bar'

export type FooParamList = {
  Foo: {}
}

interface Props {
  navigation: StackNavigationProp<FooParamList & BarParamList>
}

export function Foo({ navigation }: Props) {
  useEffect(() => {
    if (!isLoading && isLoggedIn) {
      navigation.navigate('Bar', {})
    }
  }, [isLoading, isLoggedIn, navigation])
  return (
    <Wrapper>
      <FooContent />
    </Wrapper>
  )
}

useNavigation@react-navigation/native のものを使う

これはimportの変更のみです。

-import { useNavigation } from 'react-navigation-hooks'
+import { useNavigation } from '@react-navigation/native'

Sceenには前のScreenから navigationとrouteを受け取るようになりました。

https://reactnavigation.org/docs/params#passing-params-to-a-previous-screen

そのため、Screenでは useNavigation を使わず、渡ってくるnavigationを使うようにしました。

パラメーターの受け渡しの変更、 getParamの廃止

https://reactnavigation.org/docs/upgrading-from-4.x/#the-navigation-prop

React Navigation 5 では、navigation propは2つのpropに分割されています。

  • navigation prop : navigategoBack などのヘルパーメソッドを含む
  • route prop : 現在の画面のデータを含みます。 これは 4とかだと navigation.state を使っていたものです。

また、getParam は廃止されました。

getParam は主に2つの役割がありました。

  • paramsundefined になることを防ぐ
  • params.someParam が未定義または null であった場合においてデフォルト値を設定する

これらは、TypeScriptの optional chainingnullish coalescing operators で同等のことができます。

Passing parameters to routes | React Navigation

v5ではこのようになりました。

export function Foo({ route, navigation }: Props) {
  const uniqueKey = route.params.uniqueKey
  return (
    <ScrollView>
      <Block>
        <FooContetn uniqueKey={uniqueKey} />
      </Block>
    </ScrollView>
  )
}

createAppContainer から NavigationContainer を使うように変更

React Navigation 5 では createAppContainer は廃止されました。

下記のドキュメントのように、NavigationContainer を使うように修正しただけでした。

https://reactnavigation.org/docs/upgrading-from-4.x#navigation-container

参考資料

Upgrading from 4.x | React Navigation

Compatibility layer | React Navigation

Passing parameters to routes | React Navigation

Type checking with TypeScript | React Navigation

React Navigation v5 傾向と対策 - Qiita