如何在React-Table中将数据从行传递到对话框组件?

3

这是我第一次使用react-table,决定重新设计应用程序中的一个组件。我已经接近成功了,但在尝试通过按钮单击将一些数据传递给弹出对话框时卡住了。

在我以前的表格设计中,使用MUI Table,我使用array.map()来呈现每个行条目。然后,我所需要做的就是使用props将数据传递给Dialog组件,这样每个操作按钮都会从它们各自的条目中加载数据(包括未显示的数据,如_id)。现在使用react-table,由于逻辑变化,我不知道如何实现同样的结果。编辑和删除按钮都存在,它们触发对话框的打开,但我的进展止步于此。

Entries.jsx

function Entries(props) {
  const navigate = useNavigate();

  const [openEdit, setOpenEdit] = useState(false);
  const [openDelete, setOpenDelete] = useState(false);

  const handleEdit = () => {
    setOpenEdit(true);
  };

  const handleDelete = () => {
    setOpenDelete(true);
  };

  const handleClose = () => {
    setOpenEdit(false);
    setOpenDelete(false);
  };

  const data = props.data;

  const columns = useMemo(
    () => [
      {
        Header: "#",
        id: "row",
        Cell: ({ row }) => {
          return <div>{row.index + 1}</div>;
        },
      },
      {
        Header: "Title",
        accessor: "title",
        className: "column-left",
        Cell: ({ cell: { value }, row: { original } }) => (
          <Link
            sx={{
              fontSize: "14px",
              fontWeight: 500,
              "&:hover": { color: "#00D67E" },
            }}
            color="#fff"
            underline="none"
            style={{ cursor: "pointer" }}
            onClick={() => navigate(`/details/${original.movieId}`)}
          >
            {value}
          </Link>
        ),
      },
      {
        Header: "Year",
        accessor: "year",
        className: "column-right",
      },
      {
        Header: "Rating",
        accessor: "details[0].rating",
        className: "column-right",
        Cell: ({ cell: { value } }) => (
          <Rating
            size="small"
            value={value}
            readOnly
            emptyIcon={<StarIcon fontSize="inherit" />}
          />
        ),
      },
      {
        Header: "Watched",
        accessor: "date",
        className: "column-right",
        Cell: ({ cell: { value } }) => format(new Date(value), "MMM dd, yyyy"),
      },
      {
        Header: "View Count",
        accessor: "details[0].view_count",
        className: "column-right",
      },
      {
        Header: "Review",
        accessor: "details[0].review",
        className: "column-right",
        Cell: ({ cell: { value } }) =>
          !value ? null : <CheckIcon color="primary" />,
      },
      {
        Header: "Actions",
        className: "column-right",
        Cell: () => (
          <div>
            <IconButton
              aria-label="edit"
              style={{ color: "#e0e0e0" }}
              onClick={handleEdit}
            >
              <EditIcon fontSize="small" />
            </IconButton>

            <IconButton
              aria-label="delete"
              style={{ color: "#e0e0e0", paddingRight: 0 }}
              onClick={handleDelete}
            >
              <DeleteIcon fontSize="small" />
            </IconButton>
          </div>
        ),
      },
    ],
    []
  );

  return (
    <div>
      {/* TABLE */}
      <CustomTable columns={columns} data={data} />

      {/* DIALOGS */}
      <EditDialog
        isOpen={openEdit}
        onClose={handleClose}
        id={data._id}
        movieId={data.movieId}
        date={data.date}
        rating={data.details[0].rating}
        review={data.details[0].review}
        onUpdate={props.onUpdate}
      />

      <DeleteDialog
        isOpen={openDelete}
        onClose={handleClose}
        title="Delete this entry?"
        message={
          <span>
            <strong>
              {data.title} ({data.date})
            </strong>{" "}
            will be removed. This action cannot be undone.
          </span>
        }
        onRemove={() => {
          props.onRemove(data._id, data.movieId);
        }}
      />
    </div>
  );
}

export default Entries;

后来我甚至尝试了一些东西来模仿我先前设计的逻辑,因为映射由CustomTable.jsx组件中的rows.map((row) => { prepareRow(row) }处理。于是我想出了这个:

{
        Header: "Actions",
        className: "column-right",
        Cell: () => (
          <div>
            <IconButton
              aria-label="edit"
              style={{ color: "#e0e0e0" }}
              onClick={({row}) => {
                handleEdit();

                return (
                  <EditDialog
                    isOpen={openEdit}
                    onClose={handleClose}
                    id={row._id}
                    movieId={row.movieId}
                    date={row.date}
                    rating={row.details[0].rating}
                    review={row.details[0].review}
                    onUpdate={props.onUpdate}
                  />
                );
              }}
            >
              <EditIcon fontSize="small" />
            </IconButton>

            <IconButton
              aria-label="delete"
              style={{ color: "#e0e0e0", paddingRight: 0 }}
              onClick={handleDelete}
            >
              <DeleteIcon fontSize="small" />
            </IconButton>
          </div>
        ),
      }

但它也没有起作用。它停在第一个属性处并抛出一个错误,说它无法读取属性row._id。这是我最后的尝试。

1个回答

2

很简单,您可以将所选行存储到状态中,然后在编辑和删除组件中使用它。试试这个方法:

function Entries(props) { 
  const navigate = useNavigate();
  const [openEdit, setOpenEdit] = useState(false);
  const [openDelete, setOpenDelete] = useState(false);
  const [selectedRow, setSelectedRow] = useState();

  const handleEdit = (row) => {
    setOpenEdit(true);
    setSelectedRow(row);
  };

  const handleDelete = (row) => {
    setOpenDelete(true);
    setSelectedRow(row);
  };

  const handleClose = () => {
    setOpenEdit(false);
    setOpenDelete(false);
    setSelectedRow();
  };

  const data = props.data;

  const columns = useMemo(
    () => [
      {
        Header: "#",
        id: "row",
        Cell: ({ row }) => {
          return <div>{row.index + 1}</div>;
        }
      },
      {
        Header: "Title",
        accessor: "title",
        className: "column-left",
        Cell: ({ cell: { value }, row: { original } }) => (
          <Link
            sx={{
              fontSize: "14px",
              fontWeight: 500,
              "&:hover": { color: "#00D67E" }
            }}
            color="#fff"
            underline="none"
            style={{ cursor: "pointer" }}
            onClick={() => navigate(`/details/${original.movieId}`)}
          >
            {value}
          </Link>
        )
      },
      {
        Header: "Year",
        accessor: "year",
        className: "column-right"
      },
      {
        Header: "Rating",
        accessor: "details[0].rating",
        className: "column-right",
        Cell: ({ cell: { value } }) => (
          <Rating
            size="small"
            value={value}
            readOnly
            emptyIcon={<StarIcon fontSize="inherit" />}
          />
        )
      },
      {
        Header: "Watched",
        accessor: "date",
        className: "column-right",
        Cell: ({ cell: { value } }) => format(new Date(value), "MMM dd, yyyy")
      },
      {
        Header: "View Count",
        accessor: "details[0].view_count",
        className: "column-right"
      },
      {
        Header: "Review",
        accessor: "details[0].review",
        className: "column-right",
        Cell: ({ cell: { value } }) =>
          !value ? null : <CheckIcon color="primary" />
      },
      {
        Header: "Actions",
        className: "column-right",
        Cell: ({ row }) => (
          <div>
            <IconButton
              aria-label="edit"
              style={{ color: "#e0e0e0" }}
              onClick={() => handleEdit(row.original)}
            >
              <EditIcon fontSize="small" />
            </IconButton>

            <IconButton
              aria-label="delete"
              style={{ color: "#e0e0e0", paddingRight: 0 }}
              onClick={() => handleEdit(row.original)}
            >
              <DeleteIcon fontSize="small" />
            </IconButton>
          </div>
        )
      }
    ],
    []
  );

  return (
    <div>
      {/* TABLE */}
      <CustomTable columns={columns} data={data} />

      {/* DIALOGS */}
        <EditDialog
          isOpen={openEdit}
          onClose={handleClose}
          id={selectedRow?._id}
          movieId={selectedRow?.movieId}
          date={selectedRow?.date}
          rating={selectedRow?.details[0]?.rating}
          review={selectedRow?.details[0]?.review}
          onUpdate={props.onUpdate}
        />

        <DeleteDialog
          isOpen={openDelete}
          onClose={handleClose}
          title="Delete this entry?"
          message={
            <span>
              <strong>
                {selectedRow?.title} ({selectedRow?.date})
              </strong>{" "}
              will be removed. This action cannot be undone.
            </span>
          }
          onRemove={() => {
            props.onRemove(selectedRow?._id, selectedRow?.movieId);
          }}
        />
    </div>
  );
}

export default Entries;

太棒了!谢谢!只是有一些问题,如果您不介意的话:在研究您的解决方案时,我注意到如果删除 openEdit && selectedRow && 部分,应用程序将尝试在呈现页面时立即触发对话框弹出(并且肯定会崩溃,因为尚未加载数据到状态中)。为什么会这样?难道它不应该在单击后设置 openEdit 后才触发操作吗?我不明白为什么您需要包含条件以及为什么只有在那里才起作用。 - Alex Braga
1
我认为对话框没有尝试打开,但是你正在为所有的props传递undefined值,因此当它尝试访问值时,例如selectedRow.details[0].rating,它将失败,因为undefined没有任何details[0].rating。您可以尝试删除条件,而不是传递selectedRow.details[0].rating,而是传递selectedRow?.details[0]?.rating - Vishnu Sajeev
我将使用更新后的属性来编辑我的答案,请尝试。 - Vishnu Sajeev
已经编辑过了,你也应该处理组件内部props的'undefined'值。 - Vishnu Sajeev
哦,现在我明白了。我没有想到它会在被按钮点击调用之前尝试传递和加载属性。所以这就是为什么它失败了。现在我想一想,这完全有道理,因为如果条件不存在,组件已经存在(虽然隐藏)。再次感谢 =) - Alex Braga

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接