器楽的緩怠

fuzzy note次郎

React Native で戻るボタンの挙動を改変する

背景

React Native で、スタックナビゲーションを使わずReactの要素として自前のモーダルを利用する機会がありました。

フレームワークの作法から外れて自前でナビゲーションをやるときに問題になるのは、OSが提供する機能との相互運用性です。今回の場合では、モーダルが開いているときにAndroidの戻るボタンが押されたら、前に表示していたスクリーンに戻るのではなく同一のスクリーンのままでモーダルを閉じるような動作を実現したくなります。

この記事では、React Native の BackHandler を用いて「戻る」機能の動作を制御し、モーダル等の開閉状態と繋ぎこんだ動作を実現するコードスニペットを紹介します。

環境

  • React Native 0.73
  • Expo SDK 50
  • Expo Router 3.4

直接関係があるのは React Native のバージョンのみです。

方針

React Native の機能である BackHandler を利用すると、OSで用意されている「戻る」機能の動作を制御することができます。

reactnative.dev

コード例

expo-routerが提供している useFocusEffect を利用しています。 react-navigation を使っている場合でも同じコードが動作するはずです。

// backhandler をインポート
import { BackHandler } from "react-native";

import { useFocusEffect } from 'expo-router';
import { useCallback, useState } from 'react';
// モーダルの開閉を表すステート
const [isModalOpen, setIsModalOpen] = useState(false);
  
useFocusEffect(useCallback(() => {
  const closeModal = () => {
    setIsModalOpen(false);
    return true;
  };

  // モーダルが開いているときはBackHandlerにイベントリスナを追加、モーダルが閉じている場合はnullを返す  
  const handler = isModalOpen
    ? BackHandler.addEventListener('hardwareBackPress', closeModal)
    : null;

  // イベントリスナを追加したときだけクリーンアップ時に削除する
  return () => handler?.remove();

}, [isPreviewModalOpen]));