Reactのmaterial-tableで行をクリックしたときにポップアップを表示する

# 目的
Reactのmaterial-tableで行をクリックしたときにポップアップで詳細を表示できるようにしたので、実装方法とハマった点の覚書です

# 仕様
1. テーブルの行をクリックするとポップアップを表示します
2. ポップアップでクリックした行の詳細が表示されます
3. ポップアップのOKをクリックすると、ポップアップが閉じます

![スクリーンショット 2021-03-04 22.07.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45710/1f1c5caa-f4c0-913d-f459-8b779461c60e.png)

![スクリーンショット 2021-03-04 22.08.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45710/60b4bf78-a471-41eb-5477-d0189fc605dd.png)


# 環境
- React 17.0
- material-table 1.69.2
- material-ui-core 4.11.3

# 実装
## 表とデータの表示
公式の [Remote Data Example](https://material-table.com/#/docs/features/remote-data) を使いましたので、説明は省略します

## ポップアップの実装

- ポップアップの開閉は [Material UIのDialogコンポーネントの仕様](https://material-ui.com/api/dialog/) に従い props `open` にboolean値で指定します
- ポップアップの開閉はstateで保持し、イベント発生ごとに切り替えます
   - テーブルの行がクリックされた時はtrue、OKボタンを押した時はfalse になるよう指定して開閉を切り替えます
- ポップアップに表示したいデータを state `rowData` で保持します
- ポップアップの状態は1つのstate `status` で保持し、setStateは1回で済ませます
   - `setOpen`, `setRowData` のように複数に分けてsetStateしてしまうと、その都度レンダリングが走ってしまい効率が悪いです

```
      const [status, setStatus] = React.useState({ open: false, rowData: {} });
      (ここにMaterialTableがある)
      <Dialog
        open={status.open}
        aria-labelledby="draggable-dialog-title"
        fullWidth
        maxWidth="lg"
      >
        <DialogTitle id="draggable-dialog-title">Detail</DialogTitle>
        <DialogContent>
          <DialogContentText>
            <img
              style={{ height: 36, borderRadius: "50%" }}
              src={status.rowData.avatar}
              alt={status.rowData.avatar}
            />
            <br />
            {status.rowData.first_name} {status.rowData.last_name}
            <br />
            Contact: {status.rowData.email}
          </DialogContentText>
        </DialogContent>
        <DialogActions>
          <Button
            onClick={() =>
              setStatus({ open: false, rowData: status.rowData })
            }
            color="primary"
          >
            OK
          </Button>
        </DialogActions>
      </Dialog>
```

## テーブルがクリックされたイベントの実装

- [material-tableのprops](https://material-table.com/#/docs/all-props) の `onRowClick` 関数で
- 『ポップアップの実装』の通り、ポップアップの状態はtrueにし、ポップアップの表示用にテーブルの行のデータを利用します 
- テーブルの行のデータは `onRowClick` の引数 `rowData` で取得できます

```
        onRowClick={(event, rowData) => {
          event.preventDefault();

          setStatus({ open: true, rowData: rowData });
        }}
``` 

## おまけ：ページング情報の保持

1ページあたりの表示件数を切り替えてから行をクリックし、 OKを押してポップアップを閉じると、1ページあたりの件数が5件に戻ってしまいます。

[all-props](https://material-table.com/#/docs/all-props) に `onChangeRowsPerPage` という関数があります。名前の通り1ページあたりのページ数を変えた時の制御ができるので、この関数の中でページ数を設定してstateで記憶しておきます。

```
onChangeRowsPerPage={(pageSize) => {
    setPageSize(pageSize);
}}
```


# サンプル
実装サンプルはCodeSandboxに置いておきます。
https://codesandbox.io/s/material-table-rowclick-popup-demo-3xzsm?file=/src/App.js

# 注意
性能上の問題がありますので、本記事の内容をそのまま商用に適用しないでください。

このページの方法をとると、行クリック時、OKボタン押下時に、テーブルの表示されている部分が全てレンダリングされる挙動になります。ReactのDeveloper Toolsで確認してみてください。
テーブルが無用にレンダリングされないよう、`MaterialTable` の部分をメモ化するなど対策が必要です。

[GitHub](https://github.com/mbrn/material-table/issues/152#issuecomment-453817850) の通り、DialogはMaterialTableのコンポーネントの外にある必要があるのですが、このおかげで制御が多少面倒になると思います。

material-tableを使う以上は、Material Table標準の[Details Panel](https://material-table.com/#/docs/features/detail-panel) を用いた方が良いかもしれません。

# 参考
- material-table公式 GitHub
   - https://github.com/mbrn/material-table/issues/152#issuecomment-453817850
- [material-table公式 | all Props](https://material-table.com/#/docs/all-props)
- [Material UI公式 | Dialog](https://material-ui.com/api/dialog/)