MUI(React×Material-ui)で汎用的なダイアログ画面を作る

2022年4月21日木曜日

react

t f B! P L

OKボタンや、YES / NO のボタンでユーザーに確認を促すメッセージダイアログを出すシーンは多くあり、その度に個別にダイアログを実装するのは無駄である。

そこで、汎用的に使えるダイアログコンポーネントを作ってみた。

完成イメージ

作成するコンポーネントの完成イメージは次のとおり。よくあるメッセージダイアログで、使い方の説明は不要であろう。

今回は、OKまたは、YES / NOの2種類のボタンを表示できるように、プロパティで切り替えられるようにする。

enter image description here

実装

まずは、ダイアログ側のソースである。

import {
  Box,
  Button,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle
} from '@mui/material';
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';

function CommonDialog(props: any) {

  // プロパティの受け取り
  const {
    title,
    message,
    onAccept,
    onClose,
    open,
    buttonType
  } = props;
  
  const [dialogOpen, setDialogOpen] = useState(false);

  // 承諾(OK または YES ボタンをクリック)した時
  const handleAccept = () => {
    handleClose();
    onAccept();
  };

  // ダイアログクローズ
  const handleClose = () => {
    setDialogOpen(false);
    onClose();
  };

  // openの値が変化した時
  useEffect(() => setDialogOpen(open), [open]);

  return (
    <Dialog
      open={dialogOpen}>
      <DialogTitle>
        <span>{title}</span>
      </DialogTitle>
      <DialogContent >
        <Box>
          {message}
        </Box>
      </DialogContent>
      <DialogActions>
        {buttonType == ButtonType.OkOnly &&
          <Button onClick={handleAccept}>OK</Button>
        }
        {buttonType == ButtonType.YesNo &&
          <>
            <Button onClick={handleAccept}>はい</Button>
            <Button onClick={handleClose}>いいえ</Button>
          </>
        }
      </DialogActions>
    </Dialog>
  );
}

// ボタン種別
export enum ButtonType {
  OkOnly = "OkOnly",
  YesNo = "YesNo",
}

// プロパティ
CommonDialog.propTypes = {
  title: PropTypes.string.isRequired,
  message: PropTypes.string.isRequired,
  onAccept: PropTypes.func.isRequired,
  onClose: PropTypes.func.isRequired,
  open: PropTypes.bool.isRequired,
  buttonType: PropTypes.oneOf([ButtonType.OkOnly, ButtonType.YesNo]).isRequired
};

export default CommonDialog;

次にダイアログを呼び出す側のコンポーネントを実装する。

import Button from '@mui/material/Button';
import React, { useState } from 'react';
import CommonDialog, { ButtonType } from './dialog/CommonDialog';

function Sample() {

  // ダイアログ用のstate
  const [digOpen, setDigOpen] = useState(false);

  return (
    <div>
      <CommonDialog
        title="タイトル"
        message="メッセージ本文です。"
        buttonType={ButtonType.OkOnly}
        open={digOpen}
        onAccept={() => console.log("onAccept")}
        onClose={() => setDigOpen(false)}
      />
      <Button onClick={() => setDigOpen(true)}>
        OPEN
      </Button>
    </div>
  );
}

export default Sample;

実装2(スマホ時はフルスクリーンで表示)

スマホなどの、画面サイズが小さいデバイスの場合、フルスクリーンでダイアログを表示するように改造したソースは以下である。

import { 
  Box, 
  Button, 
  Dialog, 
  DialogActions, 
  DialogContent, 
  DialogTitle, 
  useMediaQuery } from '@mui/material';
import { useTheme } from '@mui/material/styles';
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';

function CommonDialog(props: any) {

  // プロパティの受け取り
  const {
    title,
    message,
    onAccept,
    onClose,
    open,
    buttonType
  } = props;

  const theme = useTheme();
  const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
  const [dialogOpen, setDialogOpen] = useState(false);

  // 承諾(OK または YES ボタンをクリック)した時
  const handleAccept = () => {
    handleClose();
    onAccept();
  };

  // ダイアログクローズ
  const handleClose = () => {
    setDialogOpen(false);
    onClose();
  };

  // openの値が変化した時
  useEffect(() => setDialogOpen(open), [open]);

  return (
    <Dialog
      open={dialogOpen}
      maxWidth="sm"
      fullScreen={fullScreen}>
      <DialogTitle>
        <span>{title}</span>
      </DialogTitle>
      <DialogContent >
        <Box>
          {message}
        </Box>
      </DialogContent>
      <DialogActions>
        {buttonType == ButtonType.OkOnly &&
          <Button variant="contained" onClick={handleAccept}>OK</Button>
        }
        {buttonType == ButtonType.YesNo &&
          <>
            <Button variant="contained" onClick={handleAccept}>はい</Button>
            <Button color="secondary" variant="outlined" onClick={handleClose}>いいえ</Button>
          </>
        }
      </DialogActions>
    </Dialog>
  );
}

// ボタン種別
export enum ButtonType {
  OkOnly = "OkOnly",
  YesNo = "YesNo",
}

// プロパティ
CommonDialog.propTypes = {
  title: PropTypes.string.isRequired,
  message: PropTypes.string.isRequired,
  onAccept: PropTypes.func.isRequired,
  onClose: PropTypes.func.isRequired,
  open: PropTypes.bool.isRequired,
  buttonType: PropTypes.oneOf([ButtonType.OkOnly, ButtonType.YesNo]).isRequired
};

export default CommonDialog;

上のコードのポインタは、useMediaQuery で画面の横幅が 500px を下回った場合、Dialogコンポーネントの fullScreen プロパティに true を設定している部分である。

スポンサーリンク
スポンサーリンク

このブログを検索

Profile

自分の写真
Webアプリエンジニア。 日々新しい技術を追い求めてブログでアウトプットしています。
プロフィール画像は、猫村ゆゆこ様に書いてもらいました。

仕事募集もしていたり、していなかったり。

QooQ