42
42

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 5 years have passed since last update.

React-Navigatorを利用してみる(基礎編)

Last updated at Posted at 2018-10-28

注意

  • react-navigation v3で動作確認しています
  • react-navigation v4がリリースされ、createBottomTabNavigator等が分離されています(未対応)。

概要

まともなアプリを作るためにはNavigation機能の利用は避けて通れません。
ライブラリとしてはreact-navigationreact-native-router-flux(rnrf)というのが有名みたいですが、Reac自体に不慣れなので情報が多いreact-navigationを利用してみます。

この基礎編をベースにした応用編んも書きました。よろしければどうぞ。

React Navigation(react-navigation:以下RN)

RNでは3つのナビゲーション機能を提供しています。

  • Stack Navigation
  • Tab Nabigation
  • Drawer Nabigation

このそれぞれの実装方法を見てみます。

準備

作業ディレクトリの作成と必要モジュールのインストール

必要なものをインストールします。
私はexpoを利用していますが、create-react-native-appとかでも動くと思います。

expoがインストールされていない場合は、npm install -g expoで。

expo init navigation
cd navigation
npm install --save react-navigation
npm install

expo24ではreact-native-gesture-handlerに関するWarningがでます。ここを見て解決を。

デモに必要なファイルを生成

移動先として利用するページ(Screen)を先に作っておきます。

mkdir screens
cd screens
touch Tab1.js Tab2.js Stack1.js Stack2.js Single1.js Single2.js

各ファイルと役割

作成したファイルは各Navigationで以下のように利用していく予定です。

スクリーンショット 2018-10-28 7.53.08.png

各ファイル記述

とりあえず画面遷移だけ確認できればいいので、各ページ名を表示するだけの簡単な記述を行っていきます。
ページ名だけが違うほぼ同じ内容ですが、作業の基本ファイルとなるので、冗長ですが各ファイルの内容を記述しておきます。

screens/Single1.js

Single1
import React from 'react';
import { View, Text, Button } from 'react-native';

export default class Single1 extends React.Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <Text>Single1</Text>
            </View>
        );
    }
}

注意:export defaultする位置

このテンプレートでは、classの宣言と一緒にexportしていますが、後々(Reduxの利用)とかを想定すると、classの宣言とexportは分けてもいいかもしれません。

import React from 'react';
import { View, Text, Button } from 'react-native';

class Single1 extends React.Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <Text>Single1</Text>
            </View>
        );
    }
}

+export default Single1;

screens/Single2.js

Single2
import React from 'react';
import { View, Text, Button } from 'react-native';

export default class Single2 extends React.Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <Text>Single2</Text>
            </View>
        );
    }
}

screens/Stack1.js

Stack1.js
import React from 'react';
import { View, Text, Button } from 'react-native';

export default class Stack1 extends React.Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <Text>Stack1</Text>
            </View>
        );
    }
}

screens/Stack2.js

Stack2.js
import React from 'react';
import { View, Text, Button } from 'react-native';

export default class Stack2 extends React.Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <Text>Stack2</Text>
            </View>
        );
    }
}

screens/Tab1.js

Tab1.js
import React from 'react';
import { View, Text, Button } from 'react-native';

export default class Tab1 extends React.Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <Text>Tab1</Text>
            </View>
        );
    }
}

screens/Tab2.js

Tab2.js
import React from 'react';
import { View, Text, Button } from 'react-native';

export default class Tab2 extends React.Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <Text>Tab2</Text>
            </View>
        );
    }
}

App.js

記述が完了したらアプリ実行の起点となるApp.jsファイルでscreens/*以下のファイルをまとめてimportしておきます。
で、とりあえず表示を確認したいので、<Single1 />を呼び出す記述を行い動作確認をしてみます。

最近のexpoが出力するテンプレートはファンクションコンポーネントベースになっていますが(おそらくHookが追加されたため)、ここではクラスコンポーネントに書き換えています。

App.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

//import screens
import Single1 from './screens/Single1';
import Single2 from './screens/Single2';
import Stack1 from './screens/Stack1';
import Stack2 from './screens/Stack2';
import Tab1 from './screens/Tab1';
import Tab2 from './screens/Tab2';

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

動作確認

コンソールで以下のコマンドを打ちます。

expo start

Webが立ち上がるので検証したい環境で検証します。私はiOS Simulatorを選んでます。

スクリーンショット 2018-10-27 19.09.58.png

動いているようです。
ここまでに用意したテンプレートを利用して各ナビゲーション機能を試してみます。

Stack Navigatorの実装

最も基本的なStack Navigatorを試します。次へ次へと移動するようなやつです。
動作を言葉で表現しづらいですが、まあ、動かせばわかります。

以下、diffモードで差分だけ見ていきます。

動作させる際にはコード中の+は削除してくださいね。念の為。

App.js

ではStack Navigationの実装を行います。
やることは、StackNavigator(ここではStack)の定義をcreateStackNavigator()で行い、それを呼び出すだけですが、createStackNavigatorを利用するためのimport等も記述します。

後で利用するcreateBottomTabNavigatorとcreateDrawerNavigatorもimportしておきます。

なお、React-Navigation V3からAppContainerでラップする必要があります。

App.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
+import { createStackNavigator, createBottomTabNavigator, createDrawerNavigator, createAppContainer } from 'react-navigation';

//import screens
import Single1 from './screens/Single1';
import Single2 from './screens/Single2';
import Stack1 from './screens/Stack1';
import Stack2 from './screens/Stack2';
import Tab1 from './screens/Tab1';
import Tab2 from './screens/Tab2';

//stack
+const Stack = createStackNavigator(
+    {
+        Stack1: {screen:Stack1},
+        Stack2: {screen: Stack2},
+    },
+    {
+        initialRouteName: 'Stack1'
+    }
+);

export default class App extends React.Component {
    render() {

        //AppContainerでラップ
+        const Layout = createAppContainer(Stack);

        return (
            <Layout/>
        );
    }
}

screens/Stack1.js

  • 初期画面となるStack1にStack2への移動ボタンをつけます(Stack1へ戻るボタンは自動生成されます)。
Stack1.js
import React from 'react';
import { View, Text, Button } from 'react-native';

export default class Stack1 extends React.Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <Text>Stack1</Text>
                <Button
                    title='GoTo Stack2'
+                   onPress={() => this.props.navigation.navigate('Stack2')}
                />
            </View>
        );
    }
}

では動作確認してみます。

スクリーンショット 2018-10-27 19.19.22.png

できました。簡単です。

Tab Navigatorの実装

次にTab Navigatorを実装します。
上記で作成したStackのコードを残したままなので長く見えますが、数行追加するだけです。

DrawerでStackを利用するので記述を残しています。

screensの方はいじりません。

App.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { createStackNavigator, createBottomTabNavigator, createDrawerNavigator, createAppContainer } from 'react-navigation';

//import screens
import Single1 from './screens/Single1';
import Single2 from './screens/Single2';
import Stack1 from './screens/Stack1';
import Stack2 from './screens/Stack2';
import Tab1 from './screens/Tab1';
import Tab2 from './screens/Tab2';

//stack
const Stack = createStackNavigator(
    {
        Stack1: { screen: Stack1 },
        Stack2: { screen: Stack2 },
    },
    {
        initialRouteName: 'Stack1'
    }
);

+//Tab
+const Tab = createBottomTabNavigator(
+    {
+        Tab1: { screen: Tab1 },
+        Tab2: { screen: Tab2 },
+    }
+);

export default class App extends React.Component {
    render() {

        //AppContainerでラップ
+       const Layout = createAppContainer(Tab);

        return (
           <Layout />
        );
    }
}
スクリーンショット 2018-10-27 19.22.29.png

出来ました。こちらも簡単でした。

参考:Tabの要素にStackを入れ子にする

下記のようにするとStackをTabのコンテンツの1つとして扱うこともできます。

ここではソースは変更いませんが、試したい人はどうぞ。

App.js(抜粋)
.....
//stack
const Stack = createStackNavigator(
  {
    Stack1: { screen: Stack1 },
    Stack2: { screen: Stack2 },
  },
  {
    initialRouteName: 'Stack1'
  }
);

//tab
const Tab = createBottomTabNavigator(
  {
+    Tab1: { screen: Stack },
    Tab2: { screen: Tab2 }
  }
);
.....

参考:Tabの要素をStackNavigatorでラップする

StackNavigatorを利用するとHeader部分に(いい意味でも悪い意味でも)一定のスペースが確保されますが、素のページにはスペースが無いので、見た目がちぐはぐになるため、素ページをStackNavigatorでラップして見た目をととのえます。

例えば下記のようにします。

//Tab
const Tab = createBottomTabNavigator(
    {
        Tab1: { screen: Stack },
        Tab2: { screen: createStackNavigator({ Tab2: { screen: Tab2 } }) },
    }
);

Drawer Navigator

最後にDrawer Navigationを実装します。画面左端を右にスワイプ?すれば、出てくる画面です。
口で説明するのが難しいですね。。。まあ、動かせばわかります。横向きのタブ?的なものです。

いくつかのサンプルではヘッダ領域にハンバーガーアイコンを表示させて、それをクリックすることで表示させているものもありますが、基本、アイコン等が無くてもスワイプで開きます(ヘッダのボタンで表示させるのは、応用で書こうかと思います)。

ここでは、今までに開発したStack, Tab, Single1, Single2を全て表示させてみます。

App.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { createStackNavigator, createBottomTabNavigator, createDrawerNavigator, createAppContainer } from 'react-navigation';

//import screens
import Single1 from './screens/Single1';
import Single2 from './screens/Single2';
import Stack1 from './screens/Stack1';
import Stack2 from './screens/Stack2';
import Tab1 from './screens/Tab1';
import Tab2 from './screens/Tab2';

//stack
const Stack = createStackNavigator(
    {
        Stack1: { screen: Stack1 },
        Stack2: { screen: Stack2 },
    },
    {
        initialRouteName: 'Stack1'
    }
);

//Tab
const Tab = createBottomTabNavigator(
    {
        Tab1: { screen: Tab1 },
        Tab2: { screen: createStackNavigator({ Tab2: { screen: Tab2 } }) },
    }
);

+//drawer
+const Drawer = createDrawerNavigator(
+    {
+        Stacks: { screen: Stack },
+        Tabs: { screen: Tab },
+        Single1: { screen: Single1 },
+        Single2: { screen: Single2 },
+    },
+    {
+        initialRouteName: 'Tabs'
+    }
+);

export default class App extends React.Component {
    render() {

        //AppContainerでラップ
+       const Layout = createAppContainer(Drawer);

        return (
            <Layout />
        );
    }
}

動作確認してみます。

普通に起動したときにはただStack1が表示されるだけです。画面左端をスワイプしてください。

スクリーンショット 2018-10-27 19.28.02.png

以上です。動きました。
わかればすごく簡単なのですが、機能の依存関係などを把握するまでに時間がかかりますね。

応用編

基本編をベースにした応用編も書きました。合わせてご覧ください。

42
42
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
42
42

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?