概要
React アプリを開発しているときに、ルーティング遷移前に確認モーダルを出したい時がある。
「このページを離脱してもいいですか?」のような。
React Router の Prompt
を利用すると、簡単に確認モーダルを実装できる。
が、見た目のカスタマイズをしたい時は、少し工夫が必要になる。
実装
共通コンポーネントしての実装がこちら。
import { Location } from ‘history’; 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