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/compat
の createCompatNavigatorFactory
を使って既存の呼び出しをラップしました。
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
に置き換えて対応しました。
NavigationStackScreenProps
を StackNavigationProp
に置き換える
v5では NavigationStackScreenProps
は StackNavigationProp
となりました。
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 : navigate
や goBack
などのヘルパーメソッドを含む
route
prop : 現在の画面のデータを含みます。 これは 4とかだと navigation.state
を使っていたものです。
また、getParam
は廃止されました。
getParam
は主に2つの役割がありました。
params
が undefined
になることを防ぐ
params.someParam
が未定義または null であった場合においてデフォルト値を設定する
これらは、TypeScriptの optional chaining
と nullish 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