🔎

Reactの初学者がモーダルを実装する際に知っておくと使える情報

2024/07/13に公開

はじめに

先日、業務でReactを使ってモーダルを実装しました。今でこそコンポーネントの分け方や状態の持たせ方に悩む機会も減ってきましたが、私も初学者のころにモーダルを実装した際には色々と悩みました。今回は初学者の方がReactでモーダルを実装する際に役立ちそうな情報をまとめて見ました。

基本的な構成

以前の私の書き方

Reactを書き始めたころの私ならおそらく以下のような書き方をしていたと思います(PageとModalは本来は別ファイルに記載しています)。モーダルの開閉状態を呼び出し元で保持して、開閉の制御も呼び出し元で行なっている状態です。モーダルに限らずこの手の制御を呼び出し元で行うとreturnの中に記載するコードの量が増えていってしまい可読性が悪くなります。

// 呼び出し元のページ
const Page = () => {
    const [isOpen, setIsOpen] = useState(false);

    return (
        { isOpen ? <Modal /> : <></> }
    );
}

// モーダルコンポーネント
const Modal = () => {
    return (
        <div>
            {/* モーダルの中身 */}
        </div>
    );
}

今の私の書き方

今の私がモーダルを実装する場合は以下のように書きます。モーダルに開閉の状態を渡して、開閉の制御はモーダル自身に任せています。また、三項演算子は使わずにif文で早期returnしています。三項演算子を使う場合に比べて文字数は増えますが、「文字数が多い = 悪いコード」というわけではありません。今回のモーダルの場合ではreturn ();の中がシンプルになり読みやすくなるという良さがあるため、あえてif文を使っています。

// 呼び出し元のページ
const Page = () => {
    const [isOpen, setIsOpen] = useState(false);

    return (
        <Modal isOpen={isOpen} />
    );
}

// モーダルコンポーネント
const Modal = ({ isOpen }) => {
    if (!isOpen) {
        return <></>;
    }

    return (
        <div>
            {/* モーダルの中身 */}
        </div>
    );
}

実装方法の一例

先ほどの考え方を踏まえた上で使い方の一例をご紹介します。あくまでも簡略化した一例ですので細かい部分の記載は省略しています。

呼び出し元のページ

特定の機能に特化したSampleModalコンポーネントを呼び出します。モーダルの中身はSampleModalに記載します。開くボタンは呼び出し元に記載します。

const Page = () => {
    const [isOpen, setIsOpen] = useState(false);

    const handleCloseModal = () => {
        setIsOpen((isOpen) => !isOpen);
    }

    return (
        <div>
            <SampleModal isOpen={isOpen} handleCloseModal={handleCloseModal} />
            <button onClick={() => setIsOpen(true)}>モーダルを開く</button>
        </div>
    );
}

export default Page;

特定のモーダルコンポーネント

特定の機能に特化したモーダルコンポーネントです。開閉の制御など全てのモーダルに共通する部分はModalコンポーネントに記載します。モーダルの内容をchildrenでModalコンポーネントに渡します。

const SampleModal = ({ isOpen, handleCloseModal }) => {
    return (
        <Modal isOpen={isOpen} handleCloseModal={handleCloseModal}>
            {/* モーダルの中身 */}
        </Modal>
    );
}

export default SampleModal;

モーダルのベースとなるコンポーネント

全てのモーダルに共通する処理やスタイルを記載します。特定の機能を持つモーダルを作る際にはこちらのコンポーネントでラップしてコンポーネントを作成します。isOpenなどのpropsをバケツリレーするのが気になる方はcontextを使用しても良いと思います。

const Modal = ({ isOpen, handleCloseModal, children }) => {
    if (!isOpen) {
        return <></>;
    }

    return (
        <div>
            <button onClick={handleCloseModal}>閉じる</button>
            { children }
        </div>
    );
}

export default Modal;

Discussion