Reactの勉強③　ポップアップ

**今回の狙い**
前回の内容で表示させていたポップアップ画面が寂しい。。。

https://qiita.com/MasahiroShouya/items/7e54f3b9e08933747d05

という事で、ポップアップ画面を作ってみたいと思います。
具体的な目標として、以下のポップアップを、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3878922/864b517c-3bdd-4612-b876-aec88c2bd1f8.png)
以下のポップアップ画面に変える事を目標とします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3878922/b5c15d71-0d95-4b92-a0a0-be7223117564.png)

**Reactのプログラム**
以下のプログラムを用意します。
```diff_conf:フォルダ構成
└── src
    ├── index.js　　　（初期起動プログラム）
    └── App.js　　　　（画面へ表示される本体）
```
```diff_javascript:index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
```
```diff_javascript:app.js
import { useState } from 'react';
import { Grid, Select, MenuItem, InputLabel, TextField, Button, Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
import { styled } from '@mui/material/styles';

// 正常の時のダイアログの見出しスタイル
const SuccessDialogTitle = styled(DialogTitle)(({ theme }) => ({
  backgroundColor: theme.palette.primary.main,
  color: theme.palette.primary.contrastText,
}));

// エラーの時のダイアログの見出しスタイル
const ErrorDialogTitle = styled(DialogTitle)(({ theme }) => ({
  backgroundColor: theme.palette.error.main,
  color: theme.palette.error.contrastText,
}));

export default function App() {

// 初期値設定
  const [showDialog, setShowDialog] = useState(false);
  const [form, setForm] = useState({ mode: 'CMPL', msg: '正常を選択しています' });
  const [dialogMessage, setDialogMessage] = useState('');
  const [isSuccess, setIsSuccess] = useState(false);

// 変更された入力値を適用する処理
  const handleForm = (e) => {
    setForm({
      ...form,
      [e.target.name]: e.target.value,
    });
  };

  // ポップアップ画面の起動処理
  const handleOpenDialog = (message, success) => {
    setDialogMessage(message);
    setIsSuccess(success);
    setShowDialog(true);
  };

  // ポップアップ画面の終了処理
  const handleCloseDialog = () => {
    setShowDialog(false);
  };

  // 実行ボタン処理
  const onsubmit = () => {
    console.log(`a:${form.mode}`);
    if (form.mode === "ER") {
      handleOpenDialog(form.msg, false);
    } else {
      handleOpenDialog(form.msg, true);
    }
  };

// 画面定義
  return (
    <div className="App">
      <form>
        <Grid container spacing={2}>
          <Grid size={{xs:12}}>
              <InputLabel id="mode">モード選択</InputLabel>
              <Select labelid="mode" name="mode" value={form.mode} onChange={handleForm}>
                <MenuItem value="ER">エラー</MenuItem>
                <MenuItem value="CMPL">正常</MenuItem>
              </Select>
          </Grid>
          <Grid size={{xs:12}}>
            <TextField id="msg" name="msg" label="ポップアップメッセージ" value={form.msg} onChange={handleForm} />
          </Grid>
          <Grid size={{xs:12}}>
            <Button variant="contained" color="primary" onClick={onsubmit}>実行</Button>
          </Grid>
        </Grid>
      </form>

      <Dialog open={showDialog} onClose={handleCloseDialog}>
        {isSuccess ? (
          <SuccessDialogTitle>Infomation</SuccessDialogTitle>
        ) : (
          <ErrorDialogTitle>Error</ErrorDialogTitle>
        )}
        <DialogContent>
          <p>{dialogMessage}</p>
        </DialogContent>
        <DialogActions>
          <Button onClick={handleCloseDialog} color="primary">
            閉じる
          </Button>
        </DialogActions>
      </Dialog>
    </div>
  );
}
```
実行すると、以下の画面が起動し、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3878922/0a2629e0-ffe4-49b6-9028-32dd915e95de.png)

「実行」ボタンでポップアップ画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3878922/4151879b-db3a-41ad-9ba0-43db44531c12.png)

**可読性を考える**
ある程度、動作が確立したら、少しメンテナンスも考えて可読性を上げたい。
では、どのように改造して可読性を向上させるか？　ここで少し問題が…
　＜可読性＞ ・・・ プログラムを見易く、解り易くがする事
そこで、Ａｉに可読性の良いプログラムを提案させると、
```diff_javascript:実行ボタン処理.js
  const onsubmit = () => {
    console.log(`a:${form.mode}`);
    if (form.mode === "ER") {
      handleOpenDialog(form.msg, false);
    } else {
      handleOpenDialog(form.msg, true);
    }
  };
```
この部分が、以下のように変わります。
```diff_javascript:実行ボタン処理.js
  const onsubmit = () => {
    console.log(`a:${form.mode}`);
    handleOpenDialog(form.msg, form.mode !== "ER");
  };
```
確かに、スマートになって見易くなった…と。ただ、「可読性」を考えると…？？
私のようなジジィ世代は、if文があった方が見易い（可読性が高い）となる訳です。

と、、、諸説ありますが（ほんとに多い…）、このままですと、少々メンテナンス性が低いので、ジジィ的な可読性の向上を目指します。

```diff_conf:フォルダ構成
└── src /
    ├── components /
    |    ├── Form.js
    |    └── AlertDialog.js
    ├── pages /
    |    └── App.js
    └── index.js
```

```diff_javascript:Form.js
import React from 'react';
import { Grid, Select, MenuItem, InputLabel, TextField, Button } from '@mui/material';

export default function Form({ form, handleForm, onsubmit }) {
  return (
    <form>
      <Grid container spacing={2}>
        <Grid size={{xs:12}}>
          <InputLabel id="mode">モード選択</InputLabel>
          <Select labelid="mode" name="mode" value={form.mode} onChange={handleForm}>
            <MenuItem value="ER">エラー</MenuItem>
            <MenuItem value="CMPL">正常</MenuItem>
          </Select>
        </Grid>
        <Grid size={{xs:12}}>
          <TextField id="msg" name="msg" label="ポップアップメッセージ" value={form.msg} onChange={handleForm} />
        </Grid>
        <Grid size={{xs:12}}>
          <Button variant="contained" color="primary" onClick={onsubmit}>実行</Button>
        </Grid>
      </Grid>
    </form>
  );
}
```

```diff_javascript:AlertDialog.js
import React from 'react';
import { Button, Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
import { styled } from '@mui/material/styles';

// 正常の時のダイアログの見出しスタイル
const SuccessDialogTitle = styled(DialogTitle)(({ theme }) => ({
  backgroundColor: theme.palette.primary.main,
  color: theme.palette.primary.contrastText,
}));

// エラーの時のダイアログの見出しスタイル
const ErrorDialogTitle = styled(DialogTitle)(({ theme }) => ({
  backgroundColor: theme.palette.error.main,
  color: theme.palette.error.contrastText,
}));

// ダイアログの処理（オープン処理、クローズ処理、メッセージ、エラー判定）
export default function AlertDialog({ open, onClose, message, isSuccess }) {
  return (
    <Dialog open={open} onClose={onClose}>
      {isSuccess ? (
        <SuccessDialogTitle>Infomation</SuccessDialogTitle>
      ) : (
        <ErrorDialogTitle>Error</ErrorDialogTitle>
      )}
      <DialogContent>
        <p>{message}</p>
      </DialogContent>
      <DialogActions>
        <Button onClick={onClose} color="primary">
          閉じる
        </Button>
      </DialogActions>
    </Dialog>
  );
}
```

```diff_javascript:App.js
import React, { useState } from 'react';
import Form from '../components/Form';
import AlertDialog from '../components/AlertDialog';

export default function App() {
  const [showDialog, setShowDialog] = useState(false);
  const [form, setForm] = useState({ mode: 'CMPL', msg: '正常を選択しています' });
  const [dialogMessage, setDialogMessage] = useState('');
  const [isSuccess, setIsSuccess] = useState(false);

  // 変更された入力値を適用する処理
  const handleForm = (e) => {
    setForm({
      ...form,
      [e.target.name]: e.target.value,
    });
  };

  // ポップアップ画面の起動処理
  const handleOpenDialog = (message, success) => {
    setDialogMessage(message);
    setIsSuccess(success);
    setShowDialog(true);
  };

  // ポップアップ画面の終了処理
  const handleCloseDialog = () => {
    setShowDialog(false);
  };

  // 実行ボタン処理
  const onsubmit = () => {
    console.log(`a:${form.mode}`);
    handleOpenDialog(form.msg, form.mode !== "ER");
  };

  // 画面定義
  return (
    <div className="App">
      <Form form={form} handleForm={handleForm} onsubmit={onsubmit} />
      <AlertDialog open={showDialog} onClose={handleCloseDialog} message={dialogMessage} isSuccess={isSuccess} />
    </div>
  );
}
```

```diff_javascript:index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './pages/App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
```
フォームを２つに分けて、イベントも切り出して、かなりスッキリした感じです。

以上、御疲れ様でした～。