Alex Braga
Alex Braga

Reputation: 545

How can I pass data from row to dialog component in react-table?

This is my first time using react-table, after I decided to redesign a component in my app. I'm almost there, but I found myself stuck while trying to pass some data to a dialog pop-up on button click.

In my previous table design, using MUI Table, I used array.map() to render each row entry. Then, all I needed to do was using props to pass data to the Dialog components, so every action button would load the data from their respective entries (including data not displayed, like _id). Now with react-table I don't know how to achieve the same result, due to the logic change. The Edit and Delete buttons are there, they trigger the dialog opening, but my progress ends here.

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;

Later on I even tried something to mimic the logic of my previous design, since the mapping is handled by the rows.map((row) => { prepareRow(row) } in my CustomTable.jsx component. Then I came up with this:

{
        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>
        ),
      }

But it didn't work either. It stops right at the first prop and throws an error saying it can't read properties row._id. That was my last shot.

Upvotes: 3

Views: 7845

Answers (1)

Vishnu Sajeev
Vishnu Sajeev

Reputation: 974

That is simple, you can store the selected row to the state and then use it in your Edit and Delete components. Try this

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;

Upvotes: 2

Related Questions