【React Native】react-navigation (version3.x)の手ほどき

# まえがき
react-nativeアプリを開発していて、**react-navigation(version3.x)**の基本的な使い方がわかってきたので、このライブラリを利用するにあたり知っておくと助かる情報、使い方をまとめました。react-nativeについての説明はしていないので知らない方は先にreact-nativeを調べて理解してください。抜け等あるかもしれませんので、記載しておいた方がいい内容ありましたら編集リクエスト投げてください。

# react-navigationとは
**react-navigation**は、react-nativeアプリの**ナビゲーション(ルーティング)**と**履歴**を簡単に管理できるライブラリです。ナビゲーション操作ではよく使われる**タブバー**、**ドロワー**を簡単に実装できます。

[公式サイト(https://reactnavigation.org/)](https://reactnavigation.org/)

# ナビゲーションの構造を考える
**react-navigation**で**ナビゲーション**を作る際、はじめに**構造**を考えないといけません。**構造**と言ってもわからないと思いますので、例を紹介しながら説明しようと思います。

### はじめの分岐

はじめてアプリを起動する人には**チュートリアルスクリーン**を、２回目以降は**ホームスクリーン**を表示させたいとします。そうすると構造は下記になります。

```
root（スイッチ）
　　　　-　チュートリアルスクリーン
　　　　-　ホームスクリーン
```

スイッチの中に、**チュートリアルスクリーン**と**ホームスクリーン**が同じ階層に並列に存在しているイメージです。

### タブでのスクリーン切替

ホームスクリーンでは、タブバーによって**ホームスクリーン**と**プロフィールスクリーン**と**セッティングスクリーン**に切り替えられるようにしたいとします。そうすると構造は下記になります。

```
root(スイッチ)
　　　　-　チュートリアルスクリーン
　　　　-　タブ
　　　　　　　　-　ホームスクリーン
　　　　　　　　-　プロフィールスクリーン
　　　　　　　　-　セッティングスクリーン
```

タブの中に、**ホームスクリーン**と**プロフィールスクリーン**と**セッティングスクリーン**が同じ階層に並列に存在しているイメージです。

### ホームスクリーンとプロフィールスクリーンでのスクリーン遷移

ホームスクリーンでボタンを押すと**ホーム2スクリーン**を表示、プロフィールスクリーンで編集ボタンを押すと**プロフィールエディットスクリーン**を表示したいとします。そうすると構造は下記になります。

```
root（スイッチ）
　　　　-　チュートリアルスクリーン
　　　　-　タブ
　　　　　　　　-　ホームスタック
　　　　　　　　　　　　-　ホームスクリーン
　　　　　　　　　　　　-　ホーム2スクリーン
　　　　　　　　-　プロフィールスタック
　　　　　　　　　　　　-　プロフィールスクリーン
　　　　　　　　　　　　-　プロフィールエディットスクリーン
　　　　　　　　-　セッティングスクリーン
```

ホームスタックの中に、**ホームスクリーン**と**ホーム2スクリーン**が同じ階層に並列に存在しているイメージです。

### ドロワーでのスクリーン切替

タブ内のスクリーンであれば、どこでもドロワーを開けて、ドロワーから**タームオブサービススクリーン**を表示したいとします。そうすると構造は下記になります。

```
root(スイッチ)
　　　　-　チュートリアルスクリーン
　　　　-　ドロワー
　　　　　　　　-　タブ
　　　　　　　　　　　　-　ホームスタック
　　　　　　　　　　　　　　　　-　ホームスクリーン
　　　　　　　　　　　　　　　　-　ホーム2スクリーン
　　　　　　　　　　　　-　プロフィールスタック
　　　　　　　　　　　　　　　　-　プロフィールスクリーン
　　　　　　　　　　　　　　　　-　プロフィールエディットスクリーン
　　　　　　　　　　　　-　セッティングスクリーン
　　　　　　　　-　タームオブサービススクリーン
```

ドロワーの中に、**タブ**と**タームオブサービススクリーン**が同じ階層に並列に存在しているイメージです。

### まとめ

このように、**スイッチ**と**スタック**と**タブ**と**ドロワー**を駆使して構造を作って行きます。

また基本的に、同じ階層の**ナビゲーター**、**スクリーン**同士しか切替はできません。
なので**ホームスクリーン**から**プロフィールスクリーン**に切替したい場合、一度**ホームスタック**から**プロフィールスタック**に切替し、**プロフィールスクリーン**を表示することになります。

**react-navigator**には、これらの**ナビゲーター**を簡単に作ることができる`createSwitchNavigator`、`createStackNavigator`、`createBottomTabNavigator`、`createDrawerNavigator`が用意されています。

※_上記以外にも`createMaterialTopTabNavigator`や`createTabNavigator`等もあります。_

# 準備

**CRNA(create-react-native-app)**でアプリケーションを作成します。
次に、**react-navigation**をインストールします。

```
npm install create-react-native-app -g
create-react-native-app reactNavigationSample
cd reactNavigationSample
npm install react-navigation --save

```

# ナビゲーションを実装する

構造が決まったので、必要な**スクリーン**を作成し、`react-navigation`を利用して、**スイッチ**、**スタック**、**タブ**、**ドロワー**にぶら下げていきましょう。

```
root(スイッチ)
　　　　-　チュートリアルスクリーン
　　　　-　ドロワー
　　　　　　　　-　タブ
　　　　　　　　　　　　-　ホームスタック
　　　　　　　　　　　　　　　　-　ホームスクリーン
　　　　　　　　　　　　　　　　-　ホーム2スクリーン
　　　　　　　　　　　　-　プロフィールスタック
　　　　　　　　　　　　　　　　-　プロフィールスクリーン
　　　　　　　　　　　　　　　　-　プロフィールエディットスクリーン
　　　　　　　　　　　　-　セッティングスクリーン
　　　　　　　　-　タームオブサービススクリーン
```

### スクリーンの準備

今回必要なスクリーンは**チュートリアルスクリーン**、**ホームスクリーン**、**ホーム2スクリーン**、**プロフィールスクリーン**、**プロフィールエディットスクリーン**、**セッティングスクリーン**、**タームオブサービススクリーン**です。

まず、「screens」フォルダを作成し、下記のように適当にスクリーンを作成していってください。

※_react-nativeの説明は行わないので、スクリプトの説明は省きます。_

```jsx:HomeScreen.js
import React, { Component } from 'react'
import { Platform, StyleSheet, View, Text, Button } from 'react-native'

class HomeScreen extends Component {
  render () {
    return (
      <View>
        <Text>Home</Text>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'white',
  }
})

export default HomeScreen

```

### スタックの作成

**ホームスタック**と**プロフィールスタック**を作ります。
ホームスタック内ではホームスクリーンとホーム2スクリーンの切替が可能になります。

```
root(スイッチ)
　　　　-　チュートリアルスクリーン
　　　　-　ドロワー
　　　　　　　　-　タブ
　　　　　　　　　　　　-　ホームスタック  // ここ
　　　　　　　　　　　　　　　　-　ホームスクリーン
　　　　　　　　　　　　　　　　-　ホーム2スクリーン
　　　　　　　　　　　　-　プロフィールスタック  // ここ
　　　　　　　　　　　　　　　　-　プロフィールスクリーン
　　　　　　　　　　　　　　　　-　プロフィールエディットスクリーン
　　　　　　　　　　　　-　セッティングスクリーン
　　　　　　　　-　タームオブサービススクリーン
```

「navigation」フォルダを作成し、**AppNavigator.js**を作成しましょう。

```js:AppNavigator.js
import {
  createStackNavigator,
  createBottomTabNavigator,
  createDrawerNavigator,
  createSwitchNavigator
} from 'react-navigation'

import HomeScreen from '../screens/HomeScreen'
import Home2Screen from '../screens/Home2Screen'
import ProfileScreen from '../screens/ProfileScreen'
import ProfileEditScreen from '../screens/ProfileEditScreen'

const HomeStack = createStackNavigator(
  {
    Home: {
      screen: HomeScreen
    },
    Home2: {
      screen: Home2Screen
    },
  },
  {
    initialRouteName: "Home",
  }
)

const ProfileStack = createStackNavigator(
  {
    Profile: {
      screen: ProfileScreen
    },
    ProfileEdit: {
      screen: ProfileEditScreen
    },
  },
  {
    initialRouteName: "Profile",
  }
)

```

`createStackNavigator`を使って、**スタック**を作成します。

**ホームスタック**の中には、**ホームスクリーン**と**ホーム2スクリーン**が入るので、`Home`をキーに対応するスクリーンを**ホームスクリーン**に、`Home2`をキーに対応するスクリーンを**ホーム2スクリーン**に設定しています。

`initialRouteName`で**スタック**内の初期表示スクリーンを指定できます。

**ホームスタック**では、初期表示スクリーンは**ホームスクリーン**に設定しました。

※_`Home`に対応するものはスクリーン以外でも設定でき、スタックにしたりタブにしたりできます。_

```js
const HomeStack = createStackNavigator(
  {
    Home: {
      screen: HomeScreen
    },
    Home2: {
      screen: Home2Screen
    },
  },
  {
    initialRouteName: "Home",
  }
)

```

スタック内でスクリーンの切替を行えるよう修正します。
**ホームスクリーン**、**ホーム2スクリーン**、**プロフィールスクリーン**、**プロフィールエディットスクリーン**
を下記のように修正しましょう。

```jsx:HomeScreen.js
import React, { Component } from 'react'
import { Platform, StyleSheet, View, Text, Button } from 'react-native'

class HomeScreen extends Component {
  render () {
    return (
      <View style={styles.container}>
        <Text>Home</Text>
        <Button
          title="go to Home2"
          onPress={() => this.props.navigation.navigate('Home2')}
        />
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'white',
  }
})

export default HomeScreen


```

`this.props.navigation.navigate('スクリーンに対応するキー')`でスクリーンの切替ができます。

```jsx
<Button
  title="go to Home2"
  onPress={() => this.props.navigation.navigate('Home2')}
/>

```

**スタック**が完成しました。

```
root(スイッチ)
　　　　-　チュートリアルスクリーン
　　　　-　ドロワー
　　　　　　　　-　タブ
　　　　　　　　　　　　-　ホームスタック(完成)
　　　　　　　　　　　　　　　　-　ホームスクリーン(完成)
　　　　　　　　　　　　　　　　-　ホーム2スクリーン(完成)
　　　　　　　　　　　　-　プロフィールスタック(完成)
　　　　　　　　　　　　　　　　-　プロフィールスクリーン(完成)
　　　　　　　　　　　　　　　　-　プロフィールエディットスクリーン(完成)
　　　　　　　　　　　　-　セッティングスクリーン
　　　　　　　　-　タームオブサービススクリーン
```

### タブの作成

**タブ**を作ります。
タブ内ではホームスタックとプロフィールスタックとセッティングスクリーンの切替が可能になります。

```
root(スイッチ)
　　　　-　チュートリアルスクリーン
　　　　-　ドロワー
　　　　　　　　-　タブ  // ここ
　　　　　　　　　　　　-　ホームスタック(完成)
　　　　　　　　　　　　　　　　-　ホームスクリーン(完成)
　　　　　　　　　　　　　　　　-　ホーム2スクリーン(完成)
　　　　　　　　　　　　-　プロフィールスタック(完成)
　　　　　　　　　　　　　　　　-　プロフィールスクリーン(完成)
　　　　　　　　　　　　　　　　-　プロフィールエディットスクリーン(完成)
　　　　　　　　　　　　-　セッティングスクリーン
　　　　　　　　-　タームオブサービススクリーン
```

**AppNavigator.js**に追記しましょう。

```js:AppNavigator
import {
  createStackNavigator,
  createBottomTabNavigator,
  createDrawerNavigator,
  createSwitchNavigator
} from 'react-navigation'

import HomeScreen from '../screens/HomeScreen'
import Home2Screen from '../screens/Home2Screen'
import ProfileScreen from '../screens/ProfileScreen'
import ProfileEditScreen from '../screens/ProfileEditScreen'
import SettingScreen from '../screens/SettingScreen'

const HomeStack = createStackNavigator(
  {
    Home: {
      screen: HomeScreen
    },
    Home2: {
      screen: Home2Screen
    },
  },
  {
    initialRouteName: "Home",
  }
)

const ProfileStack = createStackNavigator(
  {
    Profile: {
      screen: ProfileScreen
    },
    ProfileEdit: {
      screen: ProfileEditScreen
    },
  },
  {
    initialRouteName: "Profile",
  }
)

const Tab = createBottomTabNavigator(
  {
    Home: HomeStack,
    Profile: ProfileStack,
    Setting: {
      screen: SettingScreen,
    },
  }
)

export default Tab

```

`createBottomTabNavigator`を使って、**タブ**を作成します。

**タブ**の中には、**ホームスタック**と**プロフィールスタック**と**セッティングスクリーン**が入るので、`Home`をキーに**ホームスタック**を、`Profile`をキーに**プロフィールスタック**を、`Setting`をキーに**セッティングスクリーン**を設定しています。

最後に`Tab`を`export`しておきます。

```js
const Tab = createBottomTabNavigator(
  {
    Home: HomeStack,
    Profile: ProfileStack,
    Setting: {
      screen: SettingScreen,
    },
  }
)

export default Tab

```

**タブ**が完成しました。

```
root(スイッチ)
　　　　-　チュートリアルスクリーン
　　　　-　ドロワー
　　　　　　　　-　タブ(完成)
　　　　　　　　　　　　-　ホームスタック(完成)
　　　　　　　　　　　　　　　　-　ホームスクリーン(完成)
　　　　　　　　　　　　　　　　-　ホーム2スクリーン(完成)
　　　　　　　　　　　　-　プロフィールスタック(完成)
　　　　　　　　　　　　　　　　-　プロフィールスクリーン(完成)
　　　　　　　　　　　　　　　　-　プロフィールエディットスクリーン(完成)
　　　　　　　　　　　　-　セッティングスクリーン(完成)
　　　　　　　　-　タームオブサービススクリーン
```

### タブの動きの確認

一度動きの確認をします。

**App.js**で**タブ**を表示させるよう修正します。

```jsx:App.js
import React from 'react'
import { StyleSheet, Text, View } from 'react-native'
import { createAppContainer } from 'react-navigation'
import AppNavigator from './navigation/AppNavigator'

const AppContainer = createAppContainer(AppNavigator)

export default class App extends React.Component {
  render() {
    return <AppContainer />
  }
}

```

`createAppContainer`を使って、`AppNavigator`をコンテナ化します。

※_**react-navigation(version2.x)**では勝手にコンテナ化してくれていたみたいですが、**react-native(verison3.x)**では手動でコンテナ化してあげないと動きません。_

```js
const AppContainer = createAppContainer(AppNavigator)

```

`yarn start`で**expo**を起動させてみましょう。

<img width="300" alt="capture" src="https://qiita-image-store.s3.amazonaws.com/0/189146/8d1aa80f-b7b1-5853-dc3c-b5337a32a301.gif">

### ドロワーの作成

**ドロワー**を作ります。
ドロワー内ではタブとタームオブサービススクリーンの切替が可能になります。

```
root(スイッチ)
　　　　-　チュートリアルスクリーン
　　　　-　ドロワー  // ここ
　　　　　　　　-　タブ(完成)
　　　　　　　　　　　　-　ホームスタック(完成)
　　　　　　　　　　　　　　　　-　ホームスクリーン(完成)
　　　　　　　　　　　　　　　　-　ホーム2スクリーン(完成)
　　　　　　　　　　　　-　プロフィールスタック(完成)
　　　　　　　　　　　　　　　　-　プロフィールスクリーン(完成)
　　　　　　　　　　　　　　　　-　プロフィールエディットスクリーン(完成)
　　　　　　　　　　　　-　セッティングスクリーン(完成)
　　　　　　　　-　タームオブサービススクリーン
```

**AppNavigator.js**に追記しましょう。

```js:AppNavigator.js
import {
  createStackNavigator,
  createBottomTabNavigator,
  createDrawerNavigator,
  createSwitchNavigator
} from 'react-navigation'

import HomeScreen from '../screens/HomeScreen'
import Home2Screen from '../screens/Home2Screen'
import ProfileScreen from '../screens/ProfileScreen'
import ProfileEditScreen from '../screens/ProfileEditScreen'
import SettingScreen from '../screens/SettingScreen'

const HomeStack = createStackNavigator(
  {
    Home: {
      screen: HomeScreen
    },
    Home2: {
      screen: Home2Screen
    },
  },
  {
    initialRouteName: "Home",
  }
)

const ProfileStack = createStackNavigator(
  {
    Profile: {
      screen: ProfileScreen
    },
    ProfileEdit: {
      screen: ProfileEditScreen
    },
  },
  {
    initialRouteName: "Profile",
  }
)

const Tab = createBottomTabNavigator(
  {
    Home: HomeStack,
    Profile: ProfileStack,
    Setting: {
      screen: SettingScreen,
    },
  }
)

const Drawer = createDrawerNavigator({
  App: Tab,
  TermOfService: TermOfService,
})

export default Drawer

```

`createDrawerNavigator`を使って、**ドロワー**を作成します。

**ドロワー**の中には、**タブ**と**タームオブサービススクリーン**が入るので、`App`をキーに**タブ**を、`TermOfService`をキーに**タームオブサービススクリーン**を設定しています。

最後に`Drawer`を`export`しておきます。

```js
const Drawer = createDrawerNavigator({
  App: Tab,
  TermOfService: TermOfServiceScreen,
})

export default Drawer

```

**ドロワー**が完成しました。

```
root(スイッチ)
　　　　-　チュートリアルスクリーン
　　　　-　ドロワー(完成)
　　　　　　　　-　タブ(完成)
　　　　　　　　　　　　-　ホームスタック(完成)
　　　　　　　　　　　　　　　　-　ホームスクリーン(完成)
　　　　　　　　　　　　　　　　-　ホーム2スクリーン(完成)
　　　　　　　　　　　　-　プロフィールスタック(完成)
　　　　　　　　　　　　　　　　-　プロフィールスクリーン(完成)
　　　　　　　　　　　　　　　　-　プロフィールエディットスクリーン(完成)
　　　　　　　　　　　　-　セッティングスクリーン(完成)
　　　　　　　　-　タームオブサービススクリーン(完成)
```

### ドロワーの動きの確認

一度動きの確認をします。

`yarn start`で**expo**を起動させてみましょう。

<img width="300" alt="capture" src="https://qiita-image-store.s3.amazonaws.com/0/189146/7d1300e5-9813-c0f6-e4c1-dc88f599cb52.gif">

### スイッチの作成

**スイッチ**を作ります。
スイッチ内ではチュートリアルスクリーンとドロワーの切替が可能になります。

```
root(スイッチ)  // ここ
　　　　-　チュートリアルスクリーン
　　　　-　ドロワー(完成)
　　　　　　　　-　タブ(完成)
　　　　　　　　　　　　-　ホームスタック(完成)
　　　　　　　　　　　　　　　　-　ホームスクリーン(完成)
　　　　　　　　　　　　　　　　-　ホーム2スクリーン(完成)
　　　　　　　　　　　　-　プロフィールスタック(完成)
　　　　　　　　　　　　　　　　-　プロフィールスクリーン(完成)
　　　　　　　　　　　　　　　　-　プロフィールエディットスクリーン(完成)
　　　　　　　　　　　　-　セッティングスクリーン(完成)
　　　　　　　　-　タームオブサービススクリーン(完成)
```

**AppNavigator.js**に追記しましょう。

```js:AppNavigator.js
import {
  createStackNavigator,
  createBottomTabNavigator,
  createDrawerNavigator,
  createSwitchNavigator
} from 'react-navigation'

import HomeScreen from '../screens/HomeScreen'
import Home2Screen from '../screens/Home2Screen'
import ProfileScreen from '../screens/ProfileScreen'
import ProfileEditScreen from '../screens/ProfileEditScreen'
import SettingScreen from '../screens/SettingScreen'
import TermOfServiceScreen from '../screens/TermOfServiceScreen'
import TutorialScreen from '../screens/TutorialScreen'

const HomeStack = createStackNavigator(
  {
    Home: {
      screen: HomeScreen
    },
    Home2: {
      screen: Home2Screen
    },
  },
  {
    initialRouteName: "Home",
  }
)

const ProfileStack = createStackNavigator(
  {
    Profile: {
      screen: ProfileScreen
    },
    ProfileEdit: {
      screen: ProfileEditScreen
    },
  },
  {
    initialRouteName: "Profile",
  }
)

const Tab = createBottomTabNavigator(
  {
    Home: HomeStack,
    Profile: ProfileStack,
    Setting: {
      screen: SettingScreen,
    },
  }
)

const Drawer = createDrawerNavigator({
  App: Tab,
  TermOfService: TermOfServiceScreen,
})

const AppNavigator = createSwitchNavigator({
  Tutorial: {
    screen: TutorialScreen,
  },
  Main: Drawer,
})

export default AppNavigator

```


`createSwitchNavigator`を使って、**スイッチ**を作成します。

**スイッチ**の中には、**チュートリアルスクリーン**と**ドロワー**が入るので、`Tutorial`をキーに**チュートリアルスクリーン**を、`Main`をキーに**ドロワー**を設定しています。

最後に`AppNavigator`を`export`しておきます。

```js
const AppNavigator = createSwitchNavigator({
  Tutorial: {
    screen: TutorialScreen,
  },
  Main: Drawer,
})

export default AppNavigator

```

`Tutorial`から`Main`へで切替を行えるよう修正します。
**チュートリアルスクリーン**を下記のように修正しましょう。

```jsx:TutorialScreen.js
import React, { Component } from 'react'
import { Platform, StyleSheet, View, Text, Button } from 'react-native'

class TutorialScreen extends Component {
  render () {
    return (
      <View style={styles.container}>
        <Text>Tutorial</Text>
        <Button
          title="go to Main"
          onPress={() => this.props.navigation.navigate('Main')}
        />
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'white',
  }
})

export default TutorialScreen

```
**スタックの作成**で説明したスクリーンの切替はスクリーン以外のドロワーでも切替できます。
ようは引数にキーを入れれば切替が発生します。

`this.props.navigation.navigate('キー')`

**スイッチ**が完成しました。

```
root(スイッチ)(完成)
　　　　-　チュートリアルスクリーン(完成)
　　　　-　ドロワー(完成)
　　　　　　　　-　タブ(完成)
　　　　　　　　　　　　-　ホームスタック(完成)
　　　　　　　　　　　　　　　　-　ホームスクリーン(完成)
　　　　　　　　　　　　　　　　-　ホーム2スクリーン(完成)
　　　　　　　　　　　　-　プロフィールスタック(完成)
　　　　　　　　　　　　　　　　-　プロフィールスクリーン(完成)
　　　　　　　　　　　　　　　　-　プロフィールエディットスクリーン(完成)
　　　　　　　　　　　　-　セッティングスクリーン(完成)
　　　　　　　　-　タームオブサービススクリーン(完成)
```

### スイッチの動きの確認

最終チェックです、動きの確認をします。

`yarn start`で**expo**を起動させてみましょう。

<img width="300" alt="capture" src="https://qiita-image-store.s3.amazonaws.com/0/189146/616295da-13fd-6a3d-bc00-ea28e5b5bd07.gif">

# さいごに
はじめに構造を考えてから、`create[ XXX ]Navigator`を使って構築していけばうまく行くはずです。
react-nativeでのアプリ開発では欠かせないライブラリだと思いますので、ぜひ**react-navigation**
の使い方覚えちゃいましょう。

サンプルコードgithubにあげてます。
[https://github.com/kousaku-maron/react-navigation-sample-for-qiita](https://github.com/kousaku-maron/react-navigation-sample-for-qiita)