Andrew Irwin
Andrew Irwin

Reputation: 711

Child component rendering before props are ready

I'm fairly sure I know the problem. I think its because of the child component rendering before its props are ready. After looking at many tutorials and docs I just can't figure out what I'm doing wrong.

I am using hooks and useEffect to get Data from firestore when the page opens. after that, I want to pass the data to a child component "Table.js" to be displayed. If I have the table in the parent component it works fine, but as soon as I move the table to its own component i get an error saying forms.map is not a function `TypeError: forms.map is not a function

Here is my code for the parent component MainScreen.js

`

  function MainScreen() {
  const [forms, setForms] = useState([]);
  let history = useHistory();

  useEffect(() => {
    const fetchData = async () => {
      const unsubscribe = await firebase
        .firestore()
        .collection("")
        .doc("")
        .collection("")
        .onSnapshot(snapshot => {
          const newForms = snapshot.docs.map(doc => ({
            id: doc.id,
            ...doc.data()
          }));

          setForms(newForms);
        });
      return () => unsubscribe();
    };
    fetchData();
  }, []);



  const openSignFormPage = () => {
    history.push("/");
  };

  console.log("MainScreen forms:", forms);

  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        margin: "auto",
        padding: "2% 4%"
      }}>
      {!forms.length ? <div>Loading</div> : <MyTable forms={forms} />}

      <div
        style={{
          border: "solid red 1px",
          display: "flex",
          flexDirection: "row",
          justifyContent: "space-around"
        }}>
        <Button color={"blue"} onClick={openSignFormPage}>
          Sign forms
        </Button>
        <Button color={"blue"}>Create / Edit forms</Button>
      </div>
    </div>
  );
}
export default MainScreen;

Code for Table.js

import React, { useEffect, useState } from "react";
import Table from "@material-ui/core/Table";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import TableCell from "@material-ui/core/TableCell";
import TableBody from "@material-ui/core/TableBody";
import Paper from "@material-ui/core/Paper";
import { makeStyles } from "@material-ui/core/styles";
import { useHistory } from "react-router-dom";

const useStyles = makeStyles({
  root: {
    width: "100%",
    overflowX: "auto"
  },
  table: {
    minWidth: 650
  }
});

const MyTable = forms => {
  let history = useHistory();

  const classes = useStyles();
  //const forms = useForms("yVhc97ImwzFRz6Kh7Mbn");
  console.log("Table Page forms: ", forms);

  const openViewDisclaimerPage = row => {
    history.push("/viewform", { ...row });
  };

  return (
    <Paper className={classes.root}>
      <Table className={classes.table} aria-label="simple table">
        <TableHead>
          <TableRow>
            <TableCell>Child Name</TableCell>
            <TableCell>Time in</TableCell>
            <TableCell>Time out</TableCell>
            <TableCell>Parent Name</TableCell>
            <TableCell>shoe Box Number</TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {forms.map(row => (
            <TableRow
              hover
              onClick={() => openViewDisclaimerPage(row)}
              key={row.id}>
              <TableCell component={"th"} scope={"row"}>
                {row.children &&
                  row.children.length &&
                  row.children[0].childName}
              </TableCell>
              <TableCell>{row.timeIn}</TableCell>
              <TableCell>{row.timeOut}</TableCell>
              <TableCell>{row.parentName}</TableCell>
              <TableCell>{row.shoeBoxNumber}</TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </Paper>
  );
};

export default MyTable;

When changing the code a bit to try a different approach, it renders loading, the data then is ready, but doesn't re-render and just stays on the loading screen.

I would really appreciate any help.

Upvotes: 1

Views: 860

Answers (1)

Andres
Andres

Reputation: 1012

You have a wrong syntax to consume props:

const MyTable = forms => {
  ...
}

It should be:

const MyTable = ({ forms }) => {

You also not returning unsubscribe from your useEffect() callback. It should be:

useEffect(() => {
  ...
  return fetchData();
}, []);

or you can even remove fetchData() and use it's body instead:

useEffect(() => {
   const unsubscribe = await firebase
        .firestore()
        .collection("")
        .doc("")
        .collection("")
        .onSnapshot(snapshot => {
          const newForms = snapshot.docs.map(doc => ({
            id: doc.id,
            ...doc.data()
          }));

          setForms(newForms);
        });
      return () => unsubscribe();
  }, []);

Upvotes: 2

Related Questions