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


目的

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

仕様

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

環境

  • React 17.0
  • material-table 1.69.2
  • material-ui-core 4.11.3

実装

表とデータの表示

公式の Remote Data Example を使いましたので、説明は省略します

ポップアップの実装

  • ポップアップの開閉は Material UIの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のpropsonRowClick 関数で
  • 『ポップアップの実装』の通り、ポップアップの状態はtrueにし、ポップアップの表示用にテーブルの行のデータを利用します
  • テーブルの行のデータは onRowClick の引数 rowData で取得できます
        onRowClick={(event, rowData) => {
          event.preventDefault();

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

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

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

all-propsonChangeRowsPerPage という関数があります。名前の通り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 の通り、DialogはMaterialTableのコンポーネントの外にある必要があるのですが、このおかげで制御が多少面倒になると思います。

material-tableを使う以上は、Material Table標準のDetails Panel を用いた方が良いかもしれません。

参考