【React】押下したらモーダルを表示するカード型コンポーネントを作る【MaterialUI】

<h2>押下したらモーダルを表示するカード型コンポーネントを作りたい</h2>
設定画面などで四角いカードを配置し、押下されたときにモーダル表示するようなコンポーネントを作りたくなりました。
以下みたいなやつです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927880/10a84571-c39c-ad9f-1af5-952a10651332.png)


この記事では、まず上記のようなカード型コンポーネントを作り、MaterialUIのModalを使って、クリックしたときにModalを表示させるカード型のコンポーネントを実装していきます。

<h2>インストールするもの</h2>
今回使うのはMaterialUIの以下を使用します。
・Modal
・button
・icon

なので、初めて使うときはこちらからインストールしてください。

https://mui.com/getting-started/installation/

https://mui.com/components/icons/

あと、カードを作る際に今回はstyled-componentを採用しています。
styled-componentを使用する際は下記からインストールしてください。

https://styled-components.com/docs/basics

<h2>実装方法</h2>

<h3>モーダル本体</h3>
モーダル本体をimportしたら、そのままカード型で表示されるようにしていきます。

部品として使いまわすために、iconや表示するテキストなどの可変の部分は引数として受け取るようにします。
引数は以下の5つです。
・Icon(カードに表示するアイコン)
・text(カードに表示する文字)
・onClick(YESを押されたときに発火するfunction)
・modalTilte(モーダルで表示するタイトル)
・modalText(モーダルで表示する文章)※不要であれば、""を送ればＯＫ

また今回は、YES/NOボタンを分離せずそのまま乗っけちゃいます。こちらも可変にしておいたほうが便利かもです。

```react:modal.js
import React, { Fragment, useEffect, useState } from 'react';
import styled from "styled-components";
import { makeStyles } from '@material-ui/core/styles';
import Modal from '@material-ui/core/Modal';
import { MaterialUICommonButton } from "../MaterialUICommonButton";

const OKButtomWrapper = styled.div`
  margin-left:45%;
  float:left;
`;

const NGButtomWrapper = styled.div``;


const RectangleWrapper = styled.h2`
  border: solid;
  border-color: #F0F0F0 ;
  color:black;
  border-width:2px;
  display: flex;
  padding-left:1%;
  padding-top:1%;
  padding-bottom:1%;
  height:100%;
  width:100%;
`;

const RectangleIcon = styled.div`
  padding-top:1%;
  padding-right:1%;
`;

const RectangleText = styled.div`
  padding-top:1%;
`;


function rand() {
  return Math.round(Math.random() * 20) - 10;
}

function getModalStyle() {
  const top = 50 + rand();
  const left = 50 + rand();

  return {
    top: `${top}%`,
    left: `${left}%`,
    transform: `translate(-${top}%, -${left}%)`,
  };
}

const useStyles = makeStyles((theme) => ({
  paper: {
    position: 'absolute',
    width: 400,
    backgroundColor: theme.palette.background.paper,
    border: '2px solid #000',
    boxShadow: theme.shadows[5],
    padding: theme.spacing(2, 4, 3),
  },
}));

export default function CardModal({ Icon, text, onClick, modalTilte, modalText }) {
  const classes = useStyles();
  // getModalStyle is not a pure function, we roll the style only on the first render
  const [modalStyle] = useState(getModalStyle);
  const [open, setOpen] = useState(false);

  const handleOpen = () => {
    setOpen(true);
  };

  const handleClose = () => {
    setOpen(false);
  };

  const handleOK = () => {
    onClick()
    handleClose()
  }

  const body = (
    <div style={modalStyle} className={classes.paper}>
      <h2 id="simple-modal-title">{modalTilte}</h2>
      <p id="simple-modal-description">
        {modalText}
      </p>
      <OKButtomWrapper>
        <MaterialUICommonButton onClick={() => handleOK()} btnLabel={"OK"}></MaterialUICommonButton>
      </OKButtomWrapper>
      <NGButtomWrapper>
        <MaterialUICommonButton onClick={() => handleClose()} btnLabel={"キャンセル"}></MaterialUICommonButton>
      </NGButtomWrapper>
    </div>
  );

  return (
    <Fragment>
      <RectangleWrapper onClick={() => { handleOpen() }}>
        <RectangleIcon>
          <Icon fontSize='large' />
        </RectangleIcon>
        <RectangleText>
          {text}
        </RectangleText>
      </RectangleWrapper>
      <Modal
        open={open}
        onClose={handleClose}
        aria-labelledby="simple-modal-title"
        aria-describedby="simple-modal-description"
      >
        {body}
      </Modal>
    </Fragment>
  );
}
```

<h3>呼び出し側</h3>
以下の引数で呼び出して、表示させます。
引数に使うためのアイコンを、importしています。

```APP.js
import CardModal from "../CardModal";
import VerifiedUserIcon from '@mui/icons-material/VerifiedUser';
import MeetingRoomIcon from '@mui/icons-material/MeetingRoom';
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';

const Wrapper = styled.div`
  margin-left: 20%;
  margin-right: 20%;
  padding-top:5%;
`

export const APP = () => {

  return(
    <Wrapper>
      <CardModal
        Icon={VerifiedUserIcon}
        text={"ログアウト"}
        onClick={() => console.log("変更画面へ")}
        modalTilte={"本人確認"}
        modalText={"本人確認のため、現在登録されているメールアドレスとパスワードを確認します"}
      />
      <CardModal
        Icon={MeetingRoomIcon}
        text={"ログアウト"}
        onClick={() => console.log("ログアウト成功!")}
        modalTilte={"ログアウト"}
        modalText={"ログアウトしますか？"}
      />
      <CardModal
        Icon={MeetingRoomIcon}
        text={"アカウント削除"}
        onClick={() => console.log("アカウント削除!")}
        modalTilte={"アカウント削除"}
        modalText={"本当に削除してもよろしいですか？"}
      />
    </Wrapper>
  )

}

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927880/62582679-f1c5-4ed0-edd7-d0ff46398371.png)
押下すると、モーダルが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927880/3f2984f0-b5d6-a37a-1bae-ab01e6e270e4.png)

<h2>参考</h2>

https://mui.com/components/modal/

https://mui.com/components/material-icons/

https://mui.com/components/buttons/

https://styled-components.com/