React HooksのuseState・useRefを使ったモーダル実装についてまとめました。
今回実装するのは、以下のようなモーダルになります。
#完成コード
##React
import React, {useState, useRef} from 'react';
const App = () => {
const [modal, setModal] = useState(false)
const modalRef = useRef()
return (
<div>
<button onClick={() => setModal(true)}>Open</button>
<div className={`modal__overlay ${modal && "is-opened"}`} onClick={e=>{if(modalRef.current === e.target) setModal(false)}} ref={modalRef}>
<div className="modal__box">
<button className="modal__closeBtn" onClick={() => setModal(false)}>×</button>
<div>モーダルテキスト</div>
</div>
</div>
</div>
);
}
export default App;
##CSS
.modal__overlay{
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0,0,0,0.4);
z-index: 999;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
visibility: hidden;
transition: opacity .3s, visibility .3s;
}
.is-opened{
visibility: visible;
opacity: 1;
}
.modal__box{
background-color: #fff;
position: relative;
padding: 30px 20px;
}
.modal__closeBtn{
position: absolute;
top: 0;
right: 0;
}
#実装流れ
##1.モーダルの見た目作成
まず、オーバーレイ要素(画面全体を覆う薄暗い要素)とモーダルボックスを作成します。
import React from 'react';
const App = () => {
return (
<div>
{/* オーバーレイ要素でモーダルボックスを囲む */}
<div className="modal__overlay">
<div className="modal__box">
<div>モーダルテキスト</div>
</div>
</div>
</div>
);
}
export default App;
.modal__overlay{
/* 画面全体を覆う設定 */
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0,0,0,0.4);
z-index: 999;
/* 画面中央にモーダルを表示させる設定 */
display: flex;
align-items: center;
justify-content: center;
}
.modal__box{
background-color: #fff;
padding: 30px 20px;
}
##2.モーダルの表示切り替えの実装
次に、ボタンクリックでモーダルの表示・非表示を切り替えます。
// useState追加
import React, {useState} from 'react';
const App = () => {
// モーダルの表示・非表示をstate管理(true:表示 false:非表示)
// ※modal:現在のstate値 setModal:state値を更新するための関数 false:初期値
const [modal, setModal] = useState(false)
return (
<div>
{/* ボタンクリック後、modalをtrueに変更 */}
<button onClick={() => setModal(true)}>Open</button>
{/* modalがtrueの場合、is-openedクラスを付与 */}
<div className={`modal__overlay ${modal && "is-opened"}`}>
<div className="modal__box">
{/* ボタンクリック後、modalをfalseに変更 */}
<button className="modal__closeBtn" onClick={() => setModal(false)}>×</button>
<div>モーダルテキスト</div>
</div>
</div>
</div>
);
}
.modal__overlay{
z-index: 999;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0,0,0,0.4);
display: flex;
align-items: center;
justify-content: center;
/* 初期状態は非表示 */
opacity: 0;
visibility: hidden;
/* ゆっくり表示させる */
transition: opacity .3s, visibility .3s;
}
/* モーダル表示(modalがtrueになったら付与される) */
.is-opened{
visibility: visible;
opacity: 1;
}
.modal__box{
background-color: #fff;
padding: 30px 20px;
position: relative;
}
.modal__closeBtn{
position: absolute;
top: 0;
right: 0;
}
##3.オーバーレイクリックでモーダルを閉じる処理
最後に、オーバーレイをクリックした時にもモーダルを閉じるようにしたいと思います。
<div className={`modal__overlay ${modal && "is-opened"}`} onClick={() => setModal(false)}>
このように追記することで、オーバーレイクリック後にモーダルを閉じることができますが、これだとモーダルボックス内をクリックした時にも閉じてしまいます。
そこで、コンポーネントのDOMノードに直接アクセスできるようになるuseRefフックを使用します。
// useRef追加
import React, {useState, useRef} from 'react';
const App = () => {
// modalRefの定義 ※modalRef.currentでオーバーレイ要素を取得可能に
const modalRef = useRef()
const [modal, setModal] = useState(false)
return (
<div>
<button onClick={() => setModal(true)}>Open</button>
{/* クリックした要素がmodalRef.currentと一致した場合、modalをfalseに変更 */}
<div className={`modal__overlay ${modal && "is-opened"}`} onClick={e=>{if(modalRef.current === e.target) setModal(false)}} ref={modalRef}>
<div className="modal__box">
<button className="modal__closeBtn" onClick={() => setModal(false)}>×</button>
<div>モーダルテキスト</div>
</div>
</div>
</div>
);
}
export default App;
このようにuseState・useRefを使用することで簡単にモーダル実装ができますので、是非お試し下さい!
#参考文献
https://chaika.hatenablog.com/entry/2019/12/08/090000
https://ichiki.netlify.app/blog/20191218_react_click_out/