简体   繁体   中英

React - data doesn't render using useEffect Hook

Description

Attempting to make a reusable table component by passing data as prop. I'm making the api call in the parent component but the table won't render unless I make the api call in the table component itself or if I update the state of the object manually. Is there a way to render the data being passed through or change the state of the object so that it re-renders again?

Code

Parent Comp:

const ParentComp = () => {
  const [products, setProduct] = useState([]);

  const fetchFakeData = () => {
    TestService.getFakeData()
      .then((res) => {
        console.log(res.data);
        setProduct(res.data);
      })
      .catch((err) => {
        console.log(err);
      });
  };

  useEffect(() => {
    fetchFakeData();
  }, []);

  const productsData = useMemo(() => [...products], [products]);

  const productColumns = useMemo(
    () =>
      products[0]
        ? Object.keys(products[0])
            .filter((key) => key !== "rating")
            .map((key) => {
              return { Header: key, accessor: key };
            })
        : [],
    [products]
  );

  return (
    <div style={{ height: "80vh", paddingTop: "80px" }}>
      <Container>
        <Row>
          <h3 style={{ textAlign: "left" }}>Data Table Example</h3>

          <DataTableReuse myData={productsData} myColumns={productColumns} />
        </Row>
      </Container>
    </div>
  );
};

Table Comp:

const DataTableReuse = ({ myData, myColumns }) => {
  const [products, setProduct] = useState([]);

  useEffect(() => {
    setProduct(myData);
  }, [products]); // I manually remove and add products here manually and table will display

  const productsData = useMemo(() => [...products], [products]);

  const productColumns = useMemo(
    () =>
      products[0]
        ? Object.keys(products[0])
            .filter((key) => key !== "rating")
            .map((key) => {
              return { Header: key, accessor: key };
            })
        : [],
    [products]
  );

  const tableInstance = useTable({
    columns: productColumns,
    data: productsData,
  });

  const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
    tableInstance;

  return (
    <div style={{ overflowY: "scroll", height: "600px" }}>
      <Table striped bordered hover {...getTableProps()}>
        <thead>
          {headerGroups.map((headerGroup) => (
            <tr {...headerGroup.getHeaderGroupProps()}>
              {headerGroup.headers.map((column) => (
                <th {...column.getHeaderProps()}>{column.render("Header")}</th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody {...getTableBodyProps()}>
          {rows.map((row, i) => {
            prepareRow(row);

            return (
              <tr {...row.getRowProps()}>
                {row.cells.map((cell) => {
                  return (
                    <td {...cell.getCellProps()}>{cell.render("Cell")}</td>
                  );
                })}
              </tr>
            );
          })}
        </tbody>
      </Table>
    </div>
  );
};

What I've tried:

When I run the app locally and navigate to the page with the table component, the table will render if I remove products from the useEffect brackets or if I add it back in. But once I reload the page it goes back to being a blank table. I'm assuming this is because the products object is empty when the component renders but not sure why that's the case when I'm setting the data from prop in the useEffect hook.

Any help would be appreciated to help understand the component lifecycle here. Thank you.

Sandbox Link

https://codesandbox.io/s/awesome-bell-d5w0fe?file=/src/DataTableReuse.jsx

I was able to solve this by playing around with the useEffect hook. I tried not using the useEffect hook at all as suggested by someone but the data.table wouldn't render if I didn't set my props as state. What I ended up doing was using the useEffect hook and added my prop as useEffects second argument instead of the new state object.

const DataTableReuse = ({ myData, myColumns }) => {
  const [products, setProduct] = useState([]);

  useEffect(() => {
    setProduct(myData);
  }, [myData]); // made this watch the myData prop instead of products state.

If anyone has any suggestions on improving or if there is a better way to handle this. Please feel free to reply with an answer. Thanks all.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM