26
14

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.

React Native + Expo に、ルート管理ライブラリreact-navigationを導入する

Last updated at Posted at 2019-09-01

この記事は、「【連載】初めてのReact Native + Expo開発環境構築入門」の子記事です。環境などの条件は、親記事をご覧ください。


この記事はReact Navigation V4を前提に書かれています。2020年10月時点最新のV5対応は、こちらの兄弟記事にお任せしました。

 前々回で、画面上にメッセージが出るだけのアプリが作成できましたが、全く動きがありません。そこで今回は画面の移動(画面遷移)を実現する方法として、ルート管理ライブラリであるreact-navigationを導入します。

react-navigationの基本

 React Nativeアプリは、Reactアプリと違ってURLベースのアプリケーションではないので、ReactのようなURLベースのルート管理(つまりグローバルのdocumentオブジェクトベースのルート管理)はできません。react-navigationでは、画面を親子兄弟の構造で設計しておいて、それをもとにnavigationオブジェクトを生成し、propsとして下位画面コンポーネントに渡して使うことで、ルート動作を実現していきます。

 とりあえず、どんなコードができるのか見てみたい!という方はこちら:Our first navigate v3
 ※このサンプルのコードはreact-navigation公式ですが、古いバージョンを使っています。このまま最新のExpo + react-navigationで使うと動きません。

react-navigationをHello World!プロジェクトにインストール

 前々回までに作ったHello World!プロジェクトをVS Codeで開きます。
 Ctrl + @ でTerminalを開いて、以下のコマンドで本体のreact-navigationと、画面履歴を管理するreact-navigation-stackをインストールします。

npm install react-navigation
npm install react-navigation-stack

 さらに、react-native-gesture-handler と react-native-reanimated をインストールします。今回はExpoを使っているので、互換性があるバージョンを選択するため以下のようにnpmではなくExpoのコマンドでインストールします。

expo install react-native-gesture-handler react-native-reanimated

react-navigation-stack を使ってみる

 Webブラウザでは、<a>タグで示されたハイパーリンクをユーザーがクリックすると、画面がリンク先に遷移して、閲覧履歴にリンク先が追加されます。ここでユーザーが戻るボタンを押すと、元の画面に遷移して閲覧履歴から先のリンク先ページが削除(pop)されます。
 React Nativeではこういった閲覧履歴の概念が存在せず、react-navigation-stack はまさにこの概念を導入するためのものです。

 この動作を実現するには、画面を別個に認識することと、閲覧履歴を管理することが必要になります。それでは実際にHello World!に次の画面「Detail Screen」を作ってみましょう。Hello World!の下に、「Go to Details」ボタンを作って、ボタンを押したら「Detail Screen」に飛ぶようにします。

変更前

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

export default function App() {
  return (
    <View style={styles.container}>
      <Text>Open up App.js to start working on your app!</Text>
      <Text>Hello World!</Text>
    </View>
  );
}

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

変更後

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

class HomeScreen extends React.Component {
  render(){
    return (
      <View style={styles.container}>
        <Text>Open up App.js to start working on your app!</Text>
        <Text>Hello World!</Text>
        <Button
          title="Go to Details"
          onPress={() => this.props.navigation.navigate('Details')}
        />
      </View>
    );
  }
}

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

class DetailsScreen extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text>Details Screen</Text>
      </View>
    );
  }
}

const RootStack = createStackNavigator(
  {
    Home: HomeScreen,
    Details: DetailsScreen,
  },
  {
    initialRouteName: 'Home',
  }
);

const AppContainer = createAppContainer(RootStack);

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

変更内容を順に見ていきましょう。

必要なコンポーネントをimport

App.js
import { StyleSheet, Button, Text, View } from 'react-native';
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';

createAppContainercreateStackNavigator が使えるようにimportしています。ついでにButtonも使えるようにしています。

初期画面をコンポーネント化

App.js
class HomeScreen extends React.Component {
  render(){
    return (
      <View style={styles.container}>
...
      </View>
    );
  }
}

 初期画面をreact-navigationに1つの「画面」として認識させるため、純粋な関数で表現されていた初期画面をHomeScreenと命名してReact Componentにコンポーネント化しています。これで、のちに出てくるcreateStackNavigator()に画面を渡すことができます。

ボタンを設置

App.js
        <Button
          title="Go to Details"
          onPress={() => this.props.navigation.navigate('Details')}
        />

 React Nativeなので、<a>ではなく<Button>コンポーネントを使ってリンクボタンを作ります。リンク先は、「Details」という名前の画面です。

Details Screenの画面コンポーネントを作成

App.js
class DetailsScreen extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text>Details Screen</Text>
      </View>
    );
  }
}

 初期画面同様にReact ComponentとしてDetails Screenの画面コンポーネントを作成します。このとき、コンポーネントの名前がDetailsではなくDetailsScreenであることに注意してください。先ほど初期画面(HomeScreen)ではDetailsに飛ぶようにButtonを作りました。ところがここで作ったコンポーネントの名前は違います。名前をつなぐ作業は、このあとのcreateStackNavigatorで行います。

Stack Navigatorを作成する

App.js
const RootStack = createStackNavigator(
  {
    Home: HomeScreen,
    Details: DetailsScreen,
  },
  {
    initialRouteName: 'Home',
  }
);

 Stack Navigatorは、先に説明したように画面を認識して閲覧履歴を管理するオブジェクトです。
 ここでは初期化情報を渡してStack Navigatorを作成しています。
 第1パラメータでは、画面を定義しています。ここではHomeScreenコンポーネントをHomeという名前の画面で、DetailsScreenコンポーネントをDetailsという名前の画面で、登録しています。
 第2パラメータでは、初期画面をHomeに設定しています。これにより、ナビゲーション動作が始まったらまずHome、つまりHomeScreenコンポーネントが表示されることになります。

App本体としてStack Navigatorを登録

App.js
const AppContainer = createAppContainer(RootStack);

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

 export defaultすると、App.jsのメインを指定することになります。今回は先に作成したRootStackに画面全体を任せたいので、これをAppコンテナ化し、それを一番上のコンテナとして出力するように指定します。

実行確認

 それでは変更を保存し、実行してみましょう。

expo start

 モバイル実機からExpo Clientを実行すると、以下のようになります。

image.png

 Go to Detailsをタップすると、次の画面に移ります。
image.png

 画面移動にはちゃんとアニメーションもついていて、しかもなんと、自動的にBackボタンがついてますね!このBackボタン、もちろんちゃんと動作します。

26
14
2

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
26
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?