当サイトは、アフィリエイト広告を利用しています

【React × Emotion】モーダル画面を実装する

作成日:2022月04月11日
更新日:2024年03月02日

React + Emotionでボタン押下で画面全体に表示される
モーダル画面を作ってみたので、その実装方法をまとめておく。
この記事ではCSS in JS のEmotionを使うのでもしEmotionの使い方がよくわからない
場合は下記記事で紹介していますので見てみてください!

完成画面

先にモーダル画面の実装完了したものを載せます

React_Modal_sample2

作り方は下記から解説していきます

Modalのみを実装してみる

まずはモーダル表示のみを実装して動作を確認する。
モーダルを表示させる方法としてはCSS in JSのEmotionを使って
動的にCSSを切り替えている。

呼出し側コンポーネント

  • modalの表示フラグとそのset関数をuseStateで作る
  • modalの表示フラグでModalの表示・非表示を制御する
  • Modalコンポーネントに上記をpropsで渡してModalコンポーネント側でも表示制御をできるようにする
App.js
import "./styles.css";
import Modal from "./Modal";
import { useState } from "react";
import React from 'react';
export default function App() {
// trueになればmodal表示
const [modalFlg, setFlg] = useState(false);
return (
<>
<div className="App">
<Modal modalFlg={modalFlg} changeModal={setFlg} />
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={() => setFlg((flg) => !flg)}>modal Open!</button>
</div>
</>
);
}

モーダルコンポーネント

モーダル表示するコンポーネントを作成する。

  • propsでApp.jsxで定義したuseStateのmodalFlg, changeModalを受け取る
  • modalFlgがtrueの場合はModalを表示する※styleをemotionで制御する
  • changeModalmodalFlgを変更すればModalを閉じる
Modal.js
/** @jsxImportSource @emotion/react */
import { css } from "@emotion/react";
const Modal = ({ modalFlg, changeModal }) => {
return (
<div css={[modal(modalFlg)]}>
<button
css={[
css`
height: 20px;
`
]}
onClick={() => changeModal(flg=>!flg)}
>
close
</button>
</div>
);
};
export default Modal;
const modal = (modalFlg) => {
let opacity = 0; //透明ではない
let visibility = "hidden"; //見えない
if (modalFlg) {
// modalオープン時
opacity = 200; //透明度をあげる
visibility = "visible"; //見える
}
return [
css`
z-index: 999;
/* 定位置 */
position: fixed;
top: 0;
left: 0;
/* フルスクリーン */
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: flex-end;
/* 初期状態は非表示 */
opacity: ${opacity};
visibility: ${visibility};
/* ゆっくり表示させる */
transition: opacity 0.3s, visibility 0.3s;
`
];
};

emotionでmodalFlgを元にstyleの

  • opacity
  • visibility  

の値を設定する。

画面イメージ

動作を確認してみる

React_Modal_sample1

- ボタン押下でModalが表示できている!

応用ver

上記のmodalを使ってmodalでタグを表示し、選択したタグを
呼出し元画面に表示するサンプルを実装してみる

App.jsx

modalの呼出しコンポーネント。 Modalコンポーネントにpropsとして

  • modalの表示フラグ
  • 選択確定したタグの配列

とそのset関数を渡す。

戻り値のJSXとしては選択確定したタグがある場合は表示する。

App.jsx
import Modal from "./Modal";
import { useState } from "react";
import React from 'react';
export default function App() {
// trueになればmodal表示
const [modalFlg, setFlg] = useState(false);
// 選択確定のタグ
const [selectTag, setTag] = useState([]);
// Modalに渡すpropsをまとめる
const props = {
modalFlg,
setFlg,
selectTag,
setTag
};
return (
<>
<div style={{textAlign:"center"}}>
<Modal {...props} />
<button onClick={() => setFlg((flg) => !flg)}>modal Open!</button>
<button onClick={() => setTag([])}>clear</button>
{selectTag.length !== 0 ? (
selectTag.map((tag) => <div>{tag}</div>)
) : (
<></>
)}
</div>
</>
);
}

Moldal.jsx

Modalコンポーネント。 選択中のタグを保持するstateを宣言し、modalの表示・非表示切り替えの
タイミングで呼出し元の選択確定タグと一致させるようにする。

Moldal.jsx
/** @jsxImportSource @emotion/react */
import { css } from "@emotion/react";
import { useState, useEffect } from "react";
const Modal = ({ modalFlg, setFlg, selectTag, setTag }) => {
// 選択できるタグ一覧
const array = Array.of(
"java",
"javascript",
"gatsby",
"react",
"css",
"emotion"
);
// Modalコンポーネントで選択中のタグを保持
const [selectingTag, setSeletingTag] = useState([]);
//modalFlgが切り替わったタイミング(modal表示/非表示)で
// 選択確定タグと選択中タグの選択タグを一致させる
useEffect(() => {
setSeletingTag(() => selectTag);
}, [modalFlg]);
// タグ選択処理
const push = (e) => {
e.preventDefault();
// タグが既に選択中かチェックする
const check = selectingTag.find((tag) => {
return tag === e.target.value;
});
if (!check) {
//未選択の場合は追加
setSeletingTag((selected) => [...selectingTag, e.target.value]);
} else {
// 選択済の場合は削除
// filterで選択したタグ以外のlistを作り直す
setSeletingTag((selected) =>
selectingTag.filter((tag) => tag !== e.target.value)
);
}
};
// Modalでのタグ選択を確定する
const selectComfilm = () => {
// Modalで選択したタグを確定(呼出し元へ反映)
setTag(() => selectingTag);
// Modalを閉じる
setFlg((flg) => !flg);
};
// Modalで選択中タグをclear
const clear = () => {
// clear
setSeletingTag([]);
};
return (
<div css={[modal(modalFlg)]}>
<div>
<div css={[gridContainer]}>
{array.map((tag) => (
<button
key={tag}
value={tag}
css={[grigItem(selectingTag, tag)]}
onClick={(e) => push(e)}
>
{tag}
</button>
))}
</div>
<div css={[styles.container]}>
<button
css={[gridItemBase, styles.item]}
onClick={() => selectComfilm()}
>
select
</button>
<button css={[gridItemBase, styles.item]} onClick={() => clear()}>
clear
</button>
<button
css={[gridItemBase, styles.item]}
onClick={() => setFlg((flg) => !flg)}
>
close
</button>
</div>
</div>
</div>
);
};
export default Modal;
const modal = (modalFlg) => {
let opacity = 0; //透明ではない
let visibility = "hidden"; //不可視化
if (modalFlg) {
// modalオープン時
opacity = 200; //透明度をあげる
visibility = "visible"; //可視化
}
return [
css`
/* 一番上に表示 */
z-index: 999;
/* 定位置 */
position: fixed;
top: 0;
left: 0;
/* フルスクリーン */
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: center;
align-items: center;
/* 初期状態は非表示 */
opacity: ${opacity};
visibility: ${visibility};
/* ゆっくり表示させる */
transition: opacity 0.3s, visibility 0.3s;
/* 裏をぼかす */
backdrop-filter: blur(1px);
`
];
};
// タグ一覧をgridで表示
const gridContainer = () => [
css`
display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
gap: 20px 20px;
width: 30vw;
margin-bottom: 30px;
padding: 30px;
border-radius: 10px;
box-shadow: 0 0.25rem 1.25rem white;
`
];
// タグ選択時のスタイル変更
const grigItem = (selectTag, select) => {
// 選択したタグが既に選択済かチェック
const count = selectTag.filter((selected) => select === selected);
// 選択してない場合はスタイル追加
if (count.length !== 0) {
return [
gridItemBase,
css`
background-color: darkblue;
color: white;
`
];
} else {
return [
gridItemBase,
css`
background-color: white;
&:hover {
background-color: skyblue;
}
`
];
}
};
// タグのスタイルベース
const gridItemBase = () => [
css`
border: 0px;
border-radius: 10px;
padding: 5px;
transition: All 0.1s 0s ease-in;
&:hover {
transform: scale(1.1);
}
&:active {
background-color: darkblue;
color: white;
transform: scale(0.9);
}
`
];
// ボタンのスタイル
const styles = {
container: () => [
css`
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 10px;
`
],
item: () => [
css`
border: 1px solid black;
flex: 1;
max-width: 100px;
text-align: center;
`
]
};

画面イメージ

記事の冒頭で載せたものとなります。

React_Modal_sample2

参考

新着記事

タグ別一覧
top