32
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【v4 -> v5変更点、v5使い方】react-navigationV5でちゃった・・・// 【react-native】

Last updated at Posted at 2020-03-08

はじめに

お久しぶりです!ゆたまです!
もう管理職が板について、スプレッドシート書くか人と話すかしかしてないんじゃないかってぐらいコード書かなくなってきました。とはいえ尋常じゃなく忘れっぽいので管理職むいてないなぁっていつも思いますね。確定申告ですらシーズン来てたのを実家の税理士の叔母に「あんた、ちゃんとだしゃぁよ!」って言われて思い出したぐらいで、誰かに管理されたいっす・・・。誰か僕を管理してください。

だけどコード書くのはやっぱり楽しくまだまだtypescriptとも愛を深め合う時間が足りないのでtypescriptが寂しい思いしてるんじゃないかって不安になってるメンヘラです。

さておき

またかよ!って感じですが、react-navigationV5が出ましたね!
本を書いている関係で触らなきゃいけないんですが、ぶっちゃけいってv2->v3->v4は寝ながらでもmigrateできましたけどv5は結構めんどくさかったです。
本記事執筆段階ではまだやり終えてませんが、久々に記事を書くモチベーションなしでは流石にしんどいので記事を描きながらやっていくことにします。
この記事では主にmigrateをしながらv4との変更点を感じていきつつ、v5の使い方を解説しつつ、時折出てくる僕の今までフラストレーションを感じてもらう感じです。あと僕は基本的に「react-native init」することにこだわっているのでexpo環境ではありませんのでその点も考慮知ていただけたらと思います(依存関係など)。
では、参りまショータイム!

react-navigation

react-navigation
スクリーンショット 2020-02-09 23.52.53.png
react-nativeにおけるナビゲーションライブラリです!
おそらく、数多あるreact-native関係のルーティングライブラリの中でも安定性(仮)とできることに定評のあるライブラリで多くの開発者を支えながら、その癖と変更点で苦しめてきたライブラリではないのかと思います。

本を書いている関係で、「coming soon!!」って言われた時は、「来んな来んな来んな来んな来んな来んな来んな来んな来んな来んな来んな来んな」って思ってて、見て見ぬ振りを知ていた所、僕の師から到来の報告を受け、関わる事が確定しました。

スクリーンショット 2020-02-09 23.55.39.png

インストール

パッケージ名がそもそも変わりました。

before after
react-navigation @react-navigation/native & @react-navigation/routers
react-navigation-drawer @react-navigation/drawer
react-navigation-stack @react-navigation/stack
react-navigation-tabs @react-navigation/tabs, @react-navigation/bottom-tabsなど

一旦ココになんとなく書いておきましたが、今まではreact-navigationの中に入っていたものの別になったものや、逆にreact-navigationの中に追加されたものもありますうえすべてを記載知ているわけではないので、とりあえずなんとなくの理解でお願いします。
なので、一旦migrateするときはyarn removeからのyarn addをしてください。

また依存関係もちょっと増えているので適宜いりそうなのを追加する必要があります。
僕の場合は、react-native-screen@react-native-community/masked-viewが必要でした。
これはどんなルーティングをくんでいるかによりますが、代替みんなおんなじかと思います。

  • react-native-reanimated
  • react-native-gesture-handler
  • react-native-screens
  • react-native-safe-area-context
  • @react-native-community/masked-view

これから始める人

こんな感じで1Killできます!

yarn add @react-navigation/native
yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

iOS

cd ios
pod install
cd ..

余談:hooksについて

いままではreact-navigation-hooksを使われていた人も多いかと思われますがv5からは不要です。
yarn removeしましょう。
代わりに@react-navigation/nativeからimportしてこれます。

なんか、流行ると「チャラついて、恥ずかしくないのか」みたいなおっさん思考になってきて未だにhooksみると便利なのはわかってはいるし主流なのも理解しているけども古き良き(良いのか?)が廃れていく時流を見てみぬふりできなくなってきましたね・・・。
こんな老害無視してみなさんはhooksを使っていきましょう。

CreateNavigatorの置き換え

非常に残念ながら、CreateXXXNavigator系のAPIが大きく変更になりました。V3 -> V4のときもだったけど何回目だよっていう。
例えば、懐かしいv4のコードを振り返ってみましょう。今から始める人はこんなもの知らなくていいので、issueなどで古いコードを読まないとってならない限りはV4のところすっ飛ばしてください。覚えることは少ないほうが正です。
tabだろうがdrawerだろうが基本的にはいっしょなのでここではStackのコードだけ描きますね!。

V4

import { createStackNavigator } from 'react-navigation-stack'
import { PageA, PageB } from './pages'
import { headerStyle } from './Header'

const cardStyle = {
  backgroundColor: '#008080'
}

const StackNavigator = createStackNavigator({
  PAGE_A: {
    screen: PageA,
    navigationOptions: {
     headerStyle,
     title: 'PageA',
    },
  },
  PAGE_B: {
    screen: PageB,
    navigationOptions: {
     title: 'PageB',
    },
  }
}, {
  cardStyle,
})

こんな感じでしたね。
インターフェースは、

createStackNavigator(routes, defaultConfig)

といった感じで、routesに画面パスや画面名を、defaultConfigに共通の設定を入れることができるようになりました。

V5

ところがどっこいV5になってからは引数は廃止されています。
○ッキンって感じですが、見ていきましょう。

import React from 'react'
import { createStackNavigator } from '@react-navigation/stack'
import { PageA, PageB } from './pages'
import { headerStyle } from './Header'

const cardStyle = {
  backgroundColor: '#008080'
}

const Stack = createStackNavigator()

function PageNavigator() {
  return (
    <Stack.Navigator screenOptions={{ cardStyle }}>
      <Stack.Screen
        name="PAGE_A"
        component={PageA}
        options={{ title: 'PageA', headerLeft: () => <HeaderLeft /> }}
      />
      <Stack.Screen
        name="PAGE_B"
        component={PageB}
        options={{ title: 'PageA' }}
      />
    </Stack.Navigator>
  );
}

といった感じになります。
変更点を見ていきましょう。

import React from 'react'
import { createStackNavigator } from '@react-navigation/stack'

今まではピュアjsで良かったんですが、あたらしい記法はjsxのシンタックスが必要なので、ファイル名も書き換えてreactをインポートしておきましょう。tsの人はtsxにね!
また、StackNavigatorは@react-navigation/stackから呼び出します。

さてさて、引数のなくなったcreateStackNavigatorなんですが、空打ちしてから使います。

const Stack = createStackNavigator()

なんかめんどくさいけど、きっとインスタンスを生成させたいのだろうな感。
それではメイン部分を見ていきましょう!

function PageNavigator() {
  return (
    <Stack.Navigator screenOptions={{ cardStyle }}>
      <Stack.Screen
        name="PAGE_A"
        component={PageA}
        options={{ title: 'PageA', headerLeft: () => <HeaderLeft /> }}
      />
      <Stack.Screen
        name="PAGE_B"
        component={PageB}
        options={{ title: 'PageA' }}
      />
    </Stack.Navigator>
  );
}

こんな感じで、今まではオブジェクトで定義していたところはコンポーネントとして定義できます。
なんかずっとこれを待っていた感・・・。悔しいけど感激しちゃう・・・!!
余談ですが、この記法昔僕が大好きだったrnrfとおんなじ感じなんだよなぁ・・・。
直感的でわかりやすいですね!!

一旦、Stack.Navigatorで囲っとかって感じですね。
この時screenOptionsを使うことで子コンポーネントたちに共通のoptionsを与える事ができます。
ここではcardStyleを与えているので、

      <Stack.Screen
        name="PAGE_A"
        component={PageA}
        options={{ title: 'PageA', headerLeft: () => <HeaderLeft />, cardStyle }}
      />

としているのと同意義ですね!。
全部に与えたいものがあるときはStack.NavigatorのscreenOptionsを。この画面だけ!ってときはStack.Screenのoptionsを使ってやってください。
一応インターフェースを個人的によく使うよってものだけまとめておきますね!
全部書くととんでもなくなるので、その場合は本家ページを見てください!

NavigatorのAPI

<Stack.Navigator screenOptions={{ cardStyle }}>
プロパティ 役割
screenOptions 子コンポーネントに共通のoptionsを渡すために使用する
initialRouteName 最初に描画するScreen名を指定する
headerMode ヘッダーの表示方法を指定する Stack限定
drawerStyle ドロワーのスタイルを定義する Drawer限定
tabBarOptions タブバーに与えるオプションを定義する(これ使えばタブのデザイン変えられる) Tab限定

さてさて、つぎは画面定義ですね。
画面定義はNavigator.Screenを使って行います。

      <Stack.Screen
        name="PAGE_B"
        component={PageB}
        options={{ title: 'PageA' }}
      />

これもすごくわかりやすくてめっちゃいいですね!
おんなじようによく使うやつだけ書き出しておきますね。

ScreenのAPI

プロパティ 役割
name 画面名を指定する
component 表示させるコンポーネントを指定する
options コンポーネントに与えるオプションを定義する

こんな感じですね。
ちょっと物足りないかもですが、基本これおぼえておけばだいたい大丈夫です。

AppContainer -> NavigationContainer

AppContainerは廃止です!
かつて(V4)ではAppContainerで最後囲ってあげないといけなかったんです。
例えば、さっきのStackのNavigatorをpageNavigatorという名前にして例を書くと、

import { createAppContainer } from 'react-navigation'
import PageNavigator from './routes/pages/'

const Route = createAppContainer(PageNavigator)

といった具合に。こうするとナビゲーションが成立していました。
だけど、V5からは

import React from 'react'
import { NavigationContainer } from '@react-navigation/native'
import PageNavigator from './routes/pages/'

function Route() {
  return (
    <NavigationContainer>
       <PageNavigator />
    </NavigationContainer>
  );
}

という感じですね。
かつて存在していた、onNavigationStateChangeはonStateChangeに変わり、渡される引数も変更されてますのでいい感じに・・・!!

一番めんどくさいのがSwitchNavigator

v4使っている人、思ったよりmigrationめんどくさくないじゃんって思ってません?
ほんとに一番めんどくさいのがSwitchNavigatorです。
これなんとなくなりました・・・!!

これから始める人向けにSwitchNavigatorとは何かというと、認証などで使われるNavigatorになります。
例えば、

  • ログインページ(これ以外にも多数)
  • ホームページ(これ以外にも多数)

の2つのページがあるとします(2つとしてますが、StackNavigatorのように複数のページで構成されたものをすべて足して1つとカウントしてます)。
これってそれぞれの画面で行き来が簡単にできてはまずいですよね。
「ログイン」、「ログアウト」をちゃんとさせてから画面遷移させたいじゃないですか。そして、ログインしていないときはどういったアプリなのかとかが分かるようなページを見せたい。ホームではコンテンツを閲覧させたい。
そういった状態を元に行けるページを制限するといった用途のときに、SwitchNavigatorを使っていました。これを使うと切り分けがスムーズになるし、遷移させるコードを書かない限り、OSの機能をつかっても戻る事ができなくなります。
なのでそういった用途でこれが使われていました。
しかし、V5からは廃止でしかも代替もないです。
ではどうすればいいのかというと、

JSXで頑張れ(公式曲解)とのこと。
具体的にはContextを使って認証状態を制御してねとのことで、こんな感じになります。

function Route() {
  const authContext = React.useContext(AuthContext.Context)

  return (
    <Stack.Navigator initialRouteName="Login">
      {authContext.loggedIn ?
        <Stack.Screen name="Home"  component={Home} />:
        <Stack.Screen name="Login" component={Login} />
      }
    </Stack.Navigator>
  );
}

といった形で、三項演算子で頑張らないといけなくなるんですね・・・。
この書き換えが地味にめんどくさく、色んな状態があったりすると随所書き換えないといけなくなります。
僕の場合はこれがかなりめんどくさかったです・・・。

hooks

hooksを使う場合は@react-navigation/nativeから持ってきます。

import React from 'react';
import {Text, TouchableOpacity} from 'react-native';
import {useNavigation} from '@react-navigation/native';

function Button() {
  const {navigate} = useNavigation();
  return (
   <TouchableOpacity onPress={() => navigate('PAGE_A')} >
    <Text>go next</Text>
   </TouchableOpacity>
  );
}

従来はreact-navigation-hooksから持ってきたものを@react-navigation/nativeに置き換えるだけですね!。
めっちゃかんたんだし、標準対応していてとにかくいい以外に言いようがない感じですね。

使い方は今まで通り、

  const {navigate} = useNavigation();

navigateを手に入れて、

navigate('PAGE_A')

ページ名を引数に実行です!。これでPAGE_Aに遷移します。

Drawerのオープン

react-navigation-hooksにはopenDrawerみたいな感じの使いやすものがあったんですが、なくなりました・・・。
なのでv4の人はこの部分も置き換える必要があるとかと思います。


import React from 'react'
import { DrawerActions } from '@react-navigation/routers'
import { useNavigation } from '@react-navigation/native'
import { TouchableOpacity, Text } from 'react-native'

function HeaderLeft() { 
 const { dispatch } = useNavigation()
 const onPress = React.useCallback(() => { 
    dispatch(DrawerActions.openDrawer()) 
 }, [dispatch])
 
 return ( 
     <TouchableOpacity onPress={onPress}>
       <Text>open</Text>
     </TouchableOpacity>
    )
  }

ちょっとめんどくさい書き換えが必要がだったのはこれですね。
@react-navigation/routersからDrawerActionsを持ってきて、
至ってふつうにアクションをdispatchする形式です。これはちょっとめんどいですね・・・。

    dispatch(DrawerActions.openDrawer()) 

おわりに

やりながらメモをしていったので、結構内容端折っているところもあると思います。
要望などございましたら、ぜひぜひ追加しますので要望あればお教えください!!
なにかと結構メモっぽいものになってしまいました・・・・。時間があれば、ちゃんとした使い方をわかりやすいものを作っていきたいです。本の方ではちゃんと書いていますので見ていただけますと幸いです。

所感としてはですね、だいぶ使いやすくなったなとかんじてます。
タブに背景色をつけるとSafeAreaだけ白いままになるというバグがあって色々とめんどくさい手順を踏んでたんですがこれらのバグも解消されててわりと気持ちの良いコードを書けています(今のところは)。

本を書いたらもっとわかりやすい内容を書く時間も生まれてくると思うので、僕が分かる範囲内になりますがこういうの書いてほしいなどあったらぜひお教えください!。
また、全然コードかけないんだけどこれからかけるようになりたいという人を応援していきたいと思うのでめっちゃ入門とかも描きますし、なんなら直接教えに行きますぐらいのテンションでもありますので、まだまだ未熟者ではありますが遠慮なくツイッターなりなんなりご連絡ください。

32
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?