コンパイラかく語りき

import { Fun } from 'programming'

【React Router】ルーティング遷移前・ページ離脱前に確認モーダルをカスタマイズして表示(Prompt)

概要

React アプリを開発しているときに、ルーティング遷移前に確認モーダルを出したい時がある。

「このページを離脱してもいいですか?」のような。

React Router の Prompt を利用すると、簡単に確認モーダルを実装できる。

が、見た目のカスタマイズをしたい時は、少し工夫が必要になる。

実装

共通コンポーネントしての実装がこちら。

import { Location } fromhistory;
import React, { useEffect, useState } from ‘react’;
import { useHistory } from 'react-router'
import { Prompt } from ‘react-router-dom’;

interface Props {
  // 確認モーダル表示条件
  when: boolean;

  // location に応じて、遷移可否を判定したい場合
  shouldBlockNavigation?: (location: Location) => boolean;
}

const RouteLeavingGuard = ({
  when,
  shouldBlockNavigation = () => true,
}: Props) => {
  const history = useHistory();
  const [modalVisible, setModalVisible] = useState(false);
  const [lastLocation, setLastLocation] = useState<Location | null>(null);
  const [confirmedNavigation, setConfirmedNavigation] = useState(false);

  const closeModal = () => {
    setModalVisible(false);
  };

  // 遷移可能かどうか判定
  const handleBlockedNavigation = (nextLocation: Location): boolean => {
    if (!confirmedNavigation && shouldBlockNavigation(nextLocation)) {
      setModalVisible(true);
      setLastLocation(nextLocation);
      return false;
    }
    return true;
  };

  // 遷移可能にする
  const handleConfirmNavigationClick = () => {
    setModalVisible(false);
    setConfirmedNavigation(true);
  }; 

  useEffect(() => {
    // 遷移可能になったら遷移
    if (confirmedNavigation && lastLocation) {
      history.push(lastLocation.pathname);
    }
  }, [confirmedNavigation, lastLocation]);

  return (
    <>
      <Prompt when={when} message={handleBlockedNavigation} />
      <div className="confirm-modal" hidden={!modalVisible}> 
        <!- ここに表示したいモーダル -->
        <!- 確認ボタンなどで、handleConfirmNavigationClick を呼ぶ -->
        <!- キャンセルボタンなどで、closeModal を呼ぶ -->
      </div>
    </>
  );
};
export default RouteLeavingGuard;

利用側はこのような感じ。

import React, { useState } from ‘react’;
import RouteLeavingGuard from "path/to/component";

const App = () => {
  const [isEditing, setIsEditing] = useState(false)
  
  // 編集中なら、ページ遷移前に確認モーダルを出す、という例
  return (
    <input onChange={(e) => setIsEditing(e.target.value !== '')} />
    <RouteLeavingGuard  when={isEditing} />
  );
};

export default App;

参考

参考、というかほぼ以下の記事のコピペです…m(_ _)m