Open1

[React, Material UI] MenuItemにうまくデータを渡せなかった話

mepcodermepcoder

やりたかったこと

  • 一覧表示されたデータがあり、各行に3点のアイコン(以下3点アイコン)がある。
  • 3点アイコンをクリックすると 編集/削除ボタンが表示される。
  • 削除ボタン押下時に該当行を削除

メンバーのスキーマ定義

schema.graphql
type Member @model {
  id: ID!
  name: String!
  tel: String!
  address: String!
}

...が!

選択した行ではない行が削除される事象が発生

不具合の原因

3点アイコンクリック時に行のIDを渡していなかったため、
全行分のメニューが開き、重なって表示されていた
→どの行からメニューを開いても、操作できるメニューは最前面に表示されている行のメニューだった

うまく行った実装

変更点

  • currentRow : 現在の行を管理する
  • handleOpenMenu() : 3点アイコン押下時に現在の行のデータを currentRow にセットする
  • handleCloseMenu() : メニューが閉じられるときは currentRow を null に戻す
Listtable.tsx
const ITEM_HEIGHT = 48
const ListTable = (props) => {
  const classes = useStyles()

  const clientEditRouting = (url) => {
    Router.push(url)
  }
  const currentURL = useRouter()
  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null)
+ const [currentRow, setCurrentRow] = React.useState(null)
  const handleDelete = (e, row) => {
    e.preventDefault()
    onDeleteClicked(row.id)
  }
  const onDeleteClicked = async (id) => {
    const result = (await API.graphql(
      graphqlOperation(updateMember, {
        input: {
          id: id,
          deletedAt: Date.now().toString(),
        },
      })
    )) as GraphQLResult<UpdateMemberMutation>
    window.location.reload()
  }
-  const handleOpenMenu = (event: React.MouseEvent<HTMLElement>) => {
-    // row.id を指定していないので全行のメニューアイテムが開いてしまう
-    setAnchorEl(event.currentTarget)
-  }
-  const handleCloseMenu = () => {
-    setAnchorEl(null)
-  }
+  const handleOpenMenu = (event: React.MouseEvent<HTMLElement>, row) => {
+    setAnchorEl(event.currentTarget)
+    setCurrentRow(row)
+  }
+  const handleCloseMenu = () => {
+    setAnchorEl(null)
+    setCurrentRow(null)
+  }

  return (
    <TableContainer component={Paper}>
      <Table className={classes.table} aria-label="simple table">
        <TableHead>
          <TableRow >
            <TableCell className={classes.tableRow}>名称</TableCell>
            <TableCell className={classes.tableRow}>住所</TableCell>
            <TableCell className={classes.tableRow}>電話番号</TableCell>
            <TableCell className={classes.tableRow}></TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {props.data.map((row) => (
            <TableRow className={classes.button} key={row.id}>
              <TableCell>{row.name}</TableCell>
              <TableCell>{row.address}</TableCell>
              <TableCell>{row.tel}</TableCell>
              <TableCell>
                <IconButton
                  aria-label="more"
                  aria-controls="long-menu"
                  aria-haspopup="true"
                  onClick={(event) => handleOpenMenu(event, row)}
                  className={classes.morevertIcon}
                >
                  <MoreVertIcon />
                </IconButton>
                <Menu
                  id="long-menu"
                  anchorEl={anchorEl}
                  keepMounted
-                  open={open}
+                  open={currentRow === row}
                  onClose={() => handleCloseMenu()}
                  PaperProps={{
                    style: {
                      maxHeight: ITEM_HEIGHT * 4.5,
                      width: '20ch',
                    }
                  }}
                >
                  <MenuItem key='Edit' onClick={() => clientEditRouting(`${currentURL.pathname}/edit/${row.id}`)}>
                    <ListItemIcon>
                      <EditIcon fontSize='small' />
                    </ListItemIcon>
                    <Typography variant="inherit">編集</Typography>
                  </MenuItem>
                  <MenuItem key='Delete' onClick={e => handleDelete(e, row)}>
                    <ListItemIcon>
                      <DeleteIcon fontSize='small' />
                    </ListItemIcon>
                    <Typography variant="inherit">削除</Typography>
                  </MenuItem>
                </Menu>
              </TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  )
}

export default ListTable

結果

想定通り指定した行を削除できた。

参考

  • 3点アイコンを使った実装

https://material-ui.com/components/menus/

  • 不具合の解決のヒントになった Stack Overflow

https://stackoverflow.com/questions/66326240/how-to-pass-data-to-menuitem-event-handler