この記事は、「【連載】初めての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」に飛ぶようにします。
変更前
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',
},
});
変更後
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
import { StyleSheet, Button, Text, View } from 'react-native';
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
createAppContainer
と createStackNavigator
が使えるようにimportしています。ついでにButtonも使えるようにしています。
初期画面をコンポーネント化
class HomeScreen extends React.Component {
render(){
return (
<View style={styles.container}>
...
</View>
);
}
}
初期画面をreact-navigationに1つの「画面」として認識させるため、純粋な関数で表現されていた初期画面をHomeScreenと命名してReact Componentにコンポーネント化しています。これで、のちに出てくるcreateStackNavigator()に画面を渡すことができます。
ボタンを設置
<Button
title="Go to Details"
onPress={() => this.props.navigation.navigate('Details')}
/>
React Nativeなので、<a>
ではなく<Button>
コンポーネントを使ってリンクボタンを作ります。リンク先は、「Details」という名前の画面です。
Details Screenの画面コンポーネントを作成
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を作成する
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を登録
const AppContainer = createAppContainer(RootStack);
export default class App extends React.Component {
render() {
return <AppContainer />;
}
}
export defaultすると、App.jsのメインを指定することになります。今回は先に作成したRootStackに画面全体を任せたいので、これをAppコンテナ化し、それを一番上のコンテナとして出力するように指定します。
実行確認
それでは変更を保存し、実行してみましょう。
expo start
モバイル実機からExpo Clientを実行すると、以下のようになります。
Go to Detailsをタップすると、次の画面に移ります。
画面移動にはちゃんとアニメーションもついていて、しかもなんと、自動的にBackボタンがついてますね!このBackボタン、もちろんちゃんと動作します。