😇

【Next.js】モーダルを実装する。

2024/09/17に公開

今回は、個人的にNext.jsでよく使うモーダルの実装時メモです。

下記を含めたモーダルを実装する時に使います。

  • モーダル以外はスクロールできないようにする
  • モーダル外の部分を透過した黒背景をつける

1)モーダルを表示/非表示するボタン

"use client";

import React from "react";
import Modal from "./Modal";


export default function Main() {

    // ---------------------------------------------
    // モーダル: 表示状態
    // ---------------------------------------------
    const [isOpenModal, setIsOpenModal] = React.useState(false);

    return (
        <div>
            {/* --- ボタン --- */}
            <button onClick={()=>setIsOpenModal(true)} className="p-4 text-white font-bold bg-blue-400 rounded-xl shadow-lg">
                モーダルを開く
            </button>

            {/* --- モーダル --- */}
            <Modal isOpenModal={isOpenModal} setIsOpenModal={setIsOpenModal} />
        </div>
    );
}

2)モーダルの本体

import React, { useEffect, useRef } from "react";

export default function Modal({ isOpenModal, setIsOpenModal }: { isOpenModal: boolean, setIsOpenModal: React.Dispatch<React.SetStateAction<boolean>> }) {

    // ---------------------------------------------
    // モーダル外をクリックした時の処理
    // ---------------------------------------------
    const modalRef = useRef(null);
    useEffect(() => {
        function handleClickOutside(event: MouseEvent) {
            if (modalRef.current && !(modalRef.current as HTMLElement).contains(event.target as Node)) {
                setIsOpenModal(false);
            }
        }
        document.addEventListener("mousedown", handleClickOutside);
        return () => {
            document.removeEventListener("mousedown", handleClickOutside);
        };
    }, [modalRef, setIsOpenModal]);


    // ---------------------------------------------
    // モーダル表示中: 背面のスクロールを禁止
    // ---------------------------------------------
    useEffect(() => {
        if (isOpenModal) {
            document.body.classList.add('overflow-hidden');
        } else {
            document.body.classList.remove('overflow-hidden');
        }
    }, [isOpenModal]);

    return (
        <>
            {isOpenModal &&
                <div className="fixed z-10 top-0 left-0 w-full h-full bg-black bg-opacity-50">
                    <div className="relative z-20 top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 max-h-[95vh] md:max-h-[90vh] w-[97vw] md:w-[80vw] p-4 md:p-10 md:pb-20 bg-slate-100 border-2 border-neutral-950 shadow-lg rounded-xl overflow-auto" ref={modalRef}>

                        {/* ここにモーダルの中身 */}
                        モーダルの中身をここに書く

                    </div>
                </div>
            }
        </>

    );
}

Discussion