参照したページ
今日の環境
- ebay/nice-modal-react: 1.2.10
- next: 13.4.4
はじめに
Next.jsのアプリケーションにnice-modal-reactを使って、実用的なモーダルを実装していきます。
Next.jsのApp Routerは使わないアプリケーションにnice-modal-reactを導入する例です。
nice-modal-reactをインストールする
以下のコマンドを実行しました。
yarn add @ebay/nice-modal-react
src/components/Modal.tsxの作成
Modalコンポーネントを作成しました。
汎用的に使用するモーダルの枠です。CSSはstyled-jsxで書いています。
以下の内容で作成しました。通常のコンポーネントと違って、NiceModal.create()
の戻り値をexportしています。
import React from "react"; import css from "styled-jsx/css"; import * as NiceModal from "@ebay/nice-modal-react"; type Props = { children: React.ReactNode; isVisible: boolean; onOverlayClick: () => void; }; export const Modal = NiceModal.create(function Modal({ children, isVisible, onOverlayClick, }: Props) { const [modifierClassName, setModifierClassName] = React.useState(""); React.useEffect(() => { const modifierClassName = isVisible ? "is-visible" : ""; setModifierClassName(modifierClassName); }, [isVisible]); return ( <div className={`modal ${modifierClassName}`}> <div className="modal-overlay" onClick={onOverlayClick}> </div> {children} <style jsx>{styles}</style> </div> ); }); const styles = css` .modal { position: fixed; top: 0; left: 0; display: flex; align-items: center; justify-content: center; width: 100vw; height: 100vh; pointer-events: none; opacity: 0; transition: opacity 0.5s; } .modal.is-visible { pointer-events: initial; opacity: 1; } .modal-overlay { position: fixed; width: 100vw; height: 100vh; background-color: #000; opacity: 0.5; } `;
src/components/ExampleModalContents.tsxの作成
ExampleModalContentsコンポーネントを以下の内容で作成しました。
import React from "react"; import css from "styled-jsx/css"; type Props = { children: React.ReactNode; }; export const ExampleModalContents = React.memo(function ExampleModalContents({ children, }: Props) { return ( <div className="example-modal-contents"> {children} <style jsx>{styles}</style> </div> ); }); const styles = css` .example-modal-contents { position: fixed; width: 80vw; height: 80vh; background-color: #fff; } `;
src/pages/_app.tsx の編集
src/pages/_app.tsxには、個々の環境によって、色々書いてあるとは思いますが、以下のことをします。
- NiceModalをimport
- <NiceModal.Provider>でwrapする
例としては以下のようになります。
import type { AppProps } from "next/app"; import * as NiceModal from "@ebay/nice-modal-react"; export default function App({ Component, pageProps }: AppProps) { return ( <NiceModal.Provider> <Component {...pageProps} /> </NiceModal.Provider> ); }
src/pages/index.tsx の編集
モーダルを使用する場所はどこでも良いのですが、例として src/pages/index.tsx を以下のように編集しました。
useModal()
の引数とModalコンポーネントのid属性には同じ文字列を渡します。
import * as NiceModal from "@ebay/nice-modal-react"; import { Modal } from "@/components/Modal"; import { ExampleModalContents } from "@/components/ExampleModalContents"; export default function Home() { const modalId = "example-modal"; const modal = NiceModal.useModal(modalId); const showModal = () => { void modal.show(); }; const onCancelButtonClick = () => { void modal.hide(); }; const onApplyButtonClick = () => { void modal.hide(); }; return ( <div> <button onClick={showModal}>モーダルを開く</button> <Modal id={modalId} isVisible={modal.visible} onOverlayClick={onCancelButtonClick} > <ExampleModalContents> <button onClick={onCancelButtonClick}>キャンセル</button> <button onClick={onApplyButtonClick}>適用</button> </ExampleModalContents> </Modal> </div> ); }
modal.show()のPromiseを使いたい場合
例えば以下のようにすると、modal.show()
が返している Promise を使って後続の処理を実行することもできます。
const showModal = () => { modal .show() .then((result) => { console.log('result: ', result); }) .catch((error) => { console.log('error: ', error); }); }; const onOverlayClick = () => { modal.resolve({ message: 'resolved foo' }); void modal.hide(); };