React Nativeで画面遷移
はじめに
先日、React Nativeの開発環境をセットアップして、実際にアプリケーションを作るにはいくつかのテーマに分けて理解が必要だと思いました。
そのうち1つが画面遷移です。
ReactのWebアプリではReact Routerで画面遷移をしていました。
そもそもReact自体はSPAであるため、原則はPropsを使用して画面表示/非表示を制御するのがスタンダード、URLでの遷移に対応する場合にReact Routerという整理でアプリ開発を行っていました。
スマホアプリの場合は、URLでの遷移がないので、基本はPropsでの画面表示/非表示で十分だと思っていましたが、React Nativeでは自分でProps制御で画面表示/非表示をするのではなく、画面遷移として支援してくれるライブラリが多数あるようなので、その1つを使いたいと思います。
React Navigationを使う
今回はReact Navigationを使用します。
他の画面遷移ライブラリを使う前にスタンダードとして、1つのライブラリの使い方を理解すれば、他はその差分で理解もしやすいというのが私の考え方です。(全般的に何かを勉強する際にはこの考え方が好きです)
参考にしたサイトはこちら
React Navigationで用意されている画面遷移は大きく3通りあるようです。
- stack
- tabs
- drawer
標準的なのはstackだと思いますので、まずはこれを使ってみます。
ReactのWebアプリでは、MUIのDrawerが個人的には動きがあって好きでした。
あとあとは、React NativeでもDrawerを使った画面に挑戦したいと思います。
ライブラリインストール
まずはコアライブラリ。これは前述3つの画面遷移の共通です。
npm install @react-navigation/native
次にstackと依存関係のライブラリをインストールします。
npm install @react-navigation/native-stack
npm install react-native-screens react-native-safe-area-context
最後にpod installをします。
pod-install ios
※最初はこれをやらなかったのですが、シミュレータ実行でエラーになりましたので、必須だと思います。
※pod installが何のためのものなのか、勉強不足ですが、それはまたの機会。
試行プログラム①
試行としてApp.tsxに直接プログラミングしていきます。
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { View, Text, Button } from 'react-native';
const HomeScreen = ({ navigation }) => {
return (
<>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Detail"
onPress={() => navigation.navigate('Detail')}
/>
</View>
</>
);
}
const DetailScreen = ({ navigation }) => {
return (
<>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Detail Screen</Text>
<Button
title="Go to Detail... again"
onPress={() => navigation.navigate('Detail')}
/>
</View>
</>
);
}
const Stack = createNativeStackNavigator();
function App(): JSX.Element {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
基本構文としては、NavigationContainerの中でルーティングの記載がされ、デフォルトでは最初のルートの画面が表示される。各サブ画面はコンポーネントの扱いになるので、Propsとして「navigation」を渡すことで、各サブ画面内で画面遷移の命令が可能となる。
画面動作説明
「Go to Detail」クリックでDetail Screenが表示されます。
続けて「Go to Detail... again」クリックしても何も変わりません。
左上には1つ前の画面に戻るボタンが表示され、それをクリックすることで1つ前に戻ります。
Home Screenが最初の画面になるので、そこにはボタンが表示されません。
ポイント
navigation.navigate(ルート名)
単純な遷移。ルート名の画面が表示される。
試行プログラム②
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { View, Text, Button } from 'react-native';
const HomeScreen = ({ navigation }) => {
return (
<>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Detail"
onPress={() => navigation.navigate('Detail')}
/>
</View>
</>
);
}
const DetailScreen = ({ navigation }) => {
return (
<>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Detail Screen</Text>
<Button
title="Go to Detail... again"
onPress={() => navigation.push('Detail')}
/>
</View>
</>
);
}
const Stack = createNativeStackNavigator();
function App(): JSX.Element {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
画面動作説明
「Go to Detail」クリックでDetail Screenが表示されます。
続けて「Go to Detail... again」クリックすると、次々Detail Screenが全面に表示されていきます。
左上には1つ前の画面に戻るボタンが表示され、それをクリックすることで1つ前に戻っていきます。
Home Screenまで戻ると、最初の画面になるので、そこにはボタンが表示されません。
ポイント
navigation.push(ルート名)
ルート名の画面が前面に追加されていきます。
次々追加されていくので、戻る操作では、追加された画面分だけ戻れます。
これがstackの仕組みです。
スタックナビゲータという管理の中に、画面をpushしていくイメージです。
試行プログラム③
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { View, Text, Button } from 'react-native';
const HomeScreen = ({ navigation }) => {
return (
<>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Detail"
onPress={() => navigation.navigate('Detail')}
/>
</View>
</>
);
}
const DetailScreen = ({ navigation }) => {
return (
<>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Detail Screen</Text>
<Button
title="Go to Detail... again"
onPress={() => navigation.push('Detail')}
/>
<Button
title="Go to Home"
onPress={() => navigation.navigate('Home')}
/>
</View>
</>
);
}
const Stack = createNativeStackNavigator();
function App(): JSX.Element {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
画面動作説明
「Go to Detail」クリックでDetail Screenが表示されます。
続けて「Go to Detail... again」クリックすると、次々Detail Screenが全面に表示されていきます。
「Go to Home」クリックすると、先頭のHome Screenに戻ります。(1つ前に戻るのではなく、先頭に戻るイメージです)
ポイント
navigation.navigate(ルート名)
navigateで指定したルート名が、pushで追加されている中に既に存在する場合は、その画面まで戻る動きとなります。
スタックナビゲータで管理されている中を検索して、その中でジャンプするという考えればわかりやすいと思います。
試行プログラム④
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { View, Text, Button } from 'react-native';
const HomeScreen = ({ navigation }) => {
return (
<>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Detail"
onPress={() => navigation.navigate('Detail')}
/>
</View>
</>
);
}
const DetailScreen = ({ navigation }) => {
return (
<>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Detail Screen</Text>
<Button
title="Go to Detail... again"
onPress={() => navigation.push('Detail')}
/>
<Button
title="Go to Home"
onPress={() => navigation.navigate('Home')}
/>
<Button
title="Go to Home... again"
onPress={() => navigation.push('Home')}
/>
</View>
</>
);
}
const Stack = createNativeStackNavigator();
function App(): JSX.Element {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
画面動作説明
「Go to Detail」クリックでDetail Screenが表示されます。
続けて「Go to Detail... again」クリックすると、次々Detail Screenが全面に表示されていきます。
その後に「Go to Home... again」クリックにより、Home Screenを前面に表示します。
Home Screenの「Go to Detail」クリックすると、既に表示されていたDetail Screenに戻ります。
ポイント
navigation.navigate(ルート名)
試行③の続きで、既にpushで追加されている場合で、どの画面にジャンプするかという確認です。
pushで追加されている、ルート名と同じ画面中の、最後の画面にジャンプするという動きになります。
スタックナビゲータの中で、降順で検索されると考えればわかりやすいと思います。
navigation.goBack()
試行プログラム①〜④で、画面左上に1つ前に戻るボタンが表示されます。
これはReact Navigationでの標準機能です。(表示しないようにもできます)
一方で、自分でボタンを配置して、1つ前に戻すようにしたいという場合には、navigation.goBack()関数で同じことができます。
navigation.pop(画面数)
これもスタックナビゲータを活かした操作方法になります。
画面数を指定すると、その分だけスタックナビゲータから除外します。
つまりその画面数分だけ戻るという動きになります。
まとめ
今回はReact Navigationを使って、標準的な画面遷移を試してみました。
以前、同じクロスプラットフォームのFlutterで簡単なアプリを作ってみたことがありますが、画面遷移の考え方、pushとpopでのスタックナビゲータを操作するというのはスマホアプリならではの考え方だと思います。
Webアプリでは近い感覚で、ブラウザの進む/戻るに該当するわけですが、プログラミングする上ではスタックナビゲータを画面遷移の基本的な考え方としてイメージしていた方が作りやすいと感じました。
React Navigationには冒頭ても大きく3通りと紹介した通り、他にも画面遷移の種類が用意されています。
React Navigation以外でも、画面遷移のライブラリは公開されているので、どんどん試しながら、自分のベストプラクティスを見つけたいと思います。