ReactNativeの画面遷移

2024/11/30
reactnative-navigation

はじめに

ReactNative のドキュメントでは、画面遷移を実現するための主要なパッケージとして ReactNavigation が紹介されています

この記事では、React Navigation を使った基本的な画面遷移の実装方法について、私自身の備忘録も兼ねてまとめました。
ReactNavigation を初めて使う方や、実装時の型エラーで悩んでいる方の参考になれば幸いです。


目次

  1. ReactNavigationのインストール
  2. StackNavigatorのインストール
  3. 基本的な画面遷移
  4. 次画面への値渡し
  5. 前画面への値渡し
  6. おわりに

ReactNavigationのインストール

以下のコマンドを実行し、画面遷移に必要なパッケージをインストールします
Expo フレームワークを使用している場合は、コマンドが異なるためこちらを確認してください

npm install @react-navigation/native react-native-screens react-native-safe-area-context

ios側の設定

パッケージをリンクさせるため、以下のコマンドで pod パッケージをインストールします

npx pod-install    // pod install を実行し、pod パッケージをインストール

// 失敗する場合は、iosディレクトリで bundle exec pod install を実行
cd ios                     // iosディレクトリへ移動
bundle exec pod install    // bundlerのcocoapodsでpodパッケージをインストール

android側の設定

react-native-screens を使用できるようにするため、MainActivity を以下のように修正します

# MainActivity.kt

package com.navigationproject

import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
import com.facebook.react.defaults.DefaultReactActivityDelegate
+ import android.os.Bundle;

class MainActivity : ReactActivity() {
  // ...
+ override fun onCreate(savedInstanceState: Bundle?) {
+     super.onCreate(null)
+ }
  // ...
}

StackNavigatorのインストール

今回、StackNavigator で画面遷移を行うため、以下のコマンドを実行し、パッケージをインストールします
Expo フレームワークを使用している場合は、コマンドが異なるためこちらを確認してください

npm install @react-navigation/stack react-native-gesture-handler

react-native-gesture-handler を使用できるようにするため、2つのファイル(gesture-handler.native.js, gesture-handler.js)を作成し、エントリファイル(index.js または App.js)の先頭にインポート文を追加します

/* gesture-handler.native.js */

// Only import react-native-gesture-handler on native platforms
import 'react-native-gesture-handler'
/* gesture-handler.js */

// Don't import react-native-gesture-handler on web
/* index.js */

+ import './gesture-handler';

import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';

AppRegistry.registerComponent(appName, () => App);

パッケージをリンクさせるため、以下のコマンドで pod パッケージをインストールします

npx pod-install    // pod install を実行し、pod パッケージをインストール

// 失敗する場合は、iosディレクトリで bundle exec pod install を実行
cd ios                     // iosディレクトリへ移動
bundle exec pod install    // bundlerのcocoapodsでpodパッケージをインストール

基本的な画面遷移

基本的な画面遷移として、home 画面から second 画面への遷移を実装します
まず、home 画面と second 画面を以下のように実装します

/* ./src/screen/home/index.tsx */

import { useNavigation } from '@react-navigation/native';
import React from 'react';
import { Button, Text } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';

const Home: React.FC = () => {
    const navigation = useNavigation();

    return (
        <SafeAreaView className="flex-1 justify-center items-center">
            <Text>HomeScreen</Text>
            <Button
                onPress={() => navigation.navigate('Second', {})}
                title="Secondへ"
            />
        </SafeAreaView>
    );
};

export default Home;
/* ./src/screen/second/index.tsx */

import React from 'react';
import { Text } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';

const Second: React.FC = () => {
    return (
        <SafeAreaView className="flex-1 justify-center items-center">
            <Text>SecondScreen</Text>
        </SafeAreaView>
    );
};

export default Second;

onPress={() => navigation.navigate('Second', {})}"Second" で type エラーが発生するため、各画面のParamをまとめた型を定義し、ReactNavigation.RootParamList をグローバルで拡張します
こうすることで、type エラーを解消することができます

/* ./src/navigation/types.ts */

export type RootStackParamList = {
  Home: {}
  Second: {}
}

declare global {
  namespace ReactNavigation {
    interface RootParamList extends RootStackParamList {}
  }
}

参考;Specifying default types for useNavigation, Link, ref etc(ReactNavigation)


次に Stack.Navigator を定義し、NavigationContainer 配下に配置します

/* ./src/navigation/StackNavigator.tsx */

import { createStackNavigator } from '@react-navigation/stack';
import React from 'react';
import Home from '../screen/home';
import Second from '../screen/second';

const Stack = createStackNavigator();

const StackNavigator = () => {
    return (
        <Stack.Navigator>
            <Stack.Screen name="Home" component={Home}/>
            <Stack.Screen name="Second" component={Second}/>
        </Stack.Navigator>
    );
};

export default StackNavigator;
/* ./App.tsx */

import { NavigationContainer } from '@react-navigation/native';
import React from 'react';
import './src/assets/styles/global.css';
import StackNavigator from './src/navigation/StackNavigator';

function App(): React.JSX.Element {
  return (
    <NavigationContainer>
      <StackNavigator/>
    </NavigationContainer>
  );
}

export default App;

次画面への値渡し

まず、次画面の props を型指定するために、以下のコマンドを実行し、パッケージを追加します

npm install @react-navigation/native-stack

次に画面の param を修正します
今回は TextInput で入力した値を次の画面に渡すため、Second に {text: string} を定義します

/* ./src/navigation/types.ts */

export type RootStackParamList = {
  Home: {}
  Second: { text: string }
}
// ...

param の定義ができたので、画面を実装してきます

Home 画面では TextInput で入力された値を Second 画面へ渡すようにします
また、props の型指定を NativeStackScreenProps<RootStackParamList, 'Home'> にします
props を NativeStackScreenProps にすることで navigation を取得することができるようになるので useNavigation は削除します

/* ./src/screen/home/index.tsx */

import { NativeStackScreenProps } from '@react-navigation/native-stack';
import React, { useState } from 'react';
import { Button, Text, TextInput } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { RootStackParamList } from '../../navigation/types';

const Home: React.FC<NativeStackScreenProps<RootStackParamList, 'Home'>> = ({navigation}) => {
    const [text, onChangeText] = useState('');

     return (
        <SafeAreaView className="flex-1 justify-center items-center">
            <Text>HomeScreen</Text>
            <TextInput
                className="h-[40px] w-[200px] border-[1px] rounded-full p-3"
                onChangeText={onChangeText}
                value={text}
            />
            <Button
                onPress={() => navigation.navigate('Second', {text: text})}
                 title="Secondへ"
             />
        </SafeAreaView>
     );
 };

export default Home;

Second 画面では Home 画面から渡された text を表示します
props には Home 画面と同じく NativeStackScreenProps を指定します

/* ./src/screen/second/index.tsx */

import { NativeStackScreenProps } from '@react-navigation/native-stack';
import React from 'react';
import { Text } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { RootStackParamList } from '../../navigation/types';

const Second: React.FC<NativeStackScreenProps<RootStackParamList, 'Second'>> = ({route}) => {
    return (
        <SafeAreaView className="flex-1 justify-center items-center">
            <Text>SecondScreen</Text>
            <Text>{route.params.text}</Text>
        </SafeAreaView>
    );
};

export default Second;

参考:Type checking screens(ReactNavigation)


画面で props を受け取るようにすると Stack.Screen で型エラーが発生するため、createStackNavigator に param の型を指定します

/* ./src/navigation/StackNavigator.tsx */

import { createStackNavigator } from '@react-navigation/stack';
import React from 'react';
import Home from '../screen/home';
import Second from '../screen/second';
import { RootStackParamList } from './types';

const Stack = createStackNavigator<RootStackParamList>();

const StackNavigator = () => {
    return (
        <Stack.Navigator>
            <Stack.Screen name="Home" component={Home}/>
            <Stack.Screen name="Second" component={Second}/>
        </Stack.Navigator>
    );
};

export default StackNavigator;

以上で、次の画面に値を渡すことができます


前画面への値渡し

前画面への値渡しをする場合、navigation.popTo を使用します
param の定義方法などはこれまでと同じです

/* ./src/navigation/types.ts */

export type RootStackParamList = {
  Home: { text: string }
  Second: { text: string }
}
// ...
/* ./src/screen/home/index.tsx */

// ...
const Home: React.FC<NativeStackScreenProps<RootStackParamList, 'Home'>> = ({route, navigation}) => {
    const [text, onChangeText] = useState('');

     return (
        <SafeAreaView className="flex-1 justify-center items-center">
            <Text>HomeScreen</Text>
            <TextInput
                className="h-[40px] w-[200px] border-[1px] rounded-full p-3"
                onChangeText={onChangeText}
                value={text}
            />
            <Text>{route.params?.text}</Text>
            <Button
                onPress={() => navigation.navigate('Second', {text: text})}
                 title="Secondへ"
             />
         </SafeAreaView>
     );
 };
// ...
/* ./src/screen/second/index.tsx */

import { NativeStackScreenProps } from '@react-navigation/native-stack';
import React, { useState } from 'react';
import { Button, Text, TextInput } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { RootStackParamList } from '../../navigation/types';

const Second: React.FC<NativeStackScreenProps<RootStackParamList, 'Second'>> = ({route, navigation}) => {
    const [text, onChangeText] = useState('');

    return (
        <SafeAreaView className="flex-1 justify-center items-center">
            <Text>SecondScreen</Text>
            <TextInput
                className="h-[40px] w-[200px] border-[1px] rounded-full p-3"
                onChangeText={onChangeText}
                value={text}
            />
            <Text>{route.params.text}</Text>
            <Button
                onPress={() => navigation.popTo('Home', {text: text})}
                 title="前画面へ"
             />
        </SafeAreaView>
    );
};

export default Second;

参考:Passing params to a previous screen(ReactNavigation)


おわりに

今回の記事では、React Navigation の StackNavigator を活用した画面遷移の実装方法について解説しました

私自身、実装の過程で型エラーに悩まされることがありましたが、この記事を通じて同じような問題で困っている方のお役に立てれば幸いです

最後までお読みいただき、ありがとうございました!
この記事が少しでも皆さんの開発の参考になれば嬉しいです

Imakyo
imakyo

未経験からエンジニアへ転職。メインはSwiftとDartを用いたモバイルアプリ開発。個人開発や業務での経験を備忘録として発信していきます。