Home > database >  Why is an element found using getElementById in Next.js returning null even when document is defined
Why is an element found using getElementById in Next.js returning null even when document is defined

Time:04-28

Using SSR in my React/Next app.

Trying to find an element with the id but is returning null

even when document is present (and I can see the div with the id plTable),

and even when getElementById is called after 6 seconds to ensure the element has been loaded on screen.

What is the issue and how can I fix this?

Here is the component:

const LineItemTable: React.FC<LineItemTableProps> = ({ reportName }) => {
  const classes = useStyles({});
  const dispatch = useDispatch();
  const [page, setPage] = useState<number>(0);

  const selectedCompanyId = useSelector((state) => state.company.selectedId);
  const company = useSelector((state) => state.company.current);

  useEffect(() => {
    if (reportName && selectedCompanyId) {
      dispatch(
        getReportByName({
          name: reportName, // 'profit and loss' or 'balance sheet'
          includeLineItems: true,
          page: page,
        }),
      );
    }
  }, [reportName, selectedCompanyId]);

  let plTable: any = 'kk';

  useEffect(() => {
    console.log('uef');
    if (typeof document !== 'undefined') {
      setTimeout(() => {
        plTable = document.querySelector('plTable');// ***** NEVER FOUND ******
        console.log('doc', document); // ***** is found and defined correctly *****
        console.log('plTable', plTable); // ***** null *****
      }, 6000);
    }
  });

  const endObserver = new IntersectionObserver(
    (entries) => {
      const [entry] = entries;
      if (!entry.isIntersecting) {
        //Put what you want to happen if the end is NOT visible
        console.log('not visible');
      } else {
        //Put what you want to happen if the end is visible
        //For instance firing your function
        // setPage(page   1);
        console.log('visible');
      }
    },
    { root: null, threshold: 1 },
  );
  // endObserver.observe(plTable);

  const getLineItems = useMemo(() => makeGetAllLineItemsByReport(reportName), [
    reportName,
  ]);

  const lineItems = useSelector((state) => getLineItems(state));

  if (!lineItems) return null;

  // ADDED
  // Add an elemnt in your html with the class of "end" at the end of the chart
  // I recommend adding an empty div with the class of "end" and setting it's opacity to 0

  return (
    <div
      id="plTable" // ****** Defined here *******
      style={{
        display: 'flex',
        alignItems: 'flex-end',
        margin: 'auto 0px',
      }}
    >
      <Grid container spacing={3}>
        <Grid item xs={12}>
          <Card
            sx={{
              padding: '20px',
            }}
          >
            <CardContent
              sx={{
                alignItems: 'center',
                display: 'flex',
                height: '1000px',
              }}
            >
              <Scrollbar className={classes.scrollBar}>
                <Table className={classes.root}>
                  <TableHead>
                    <TableRow>
                      <th>
                        <TableCell className={classes.headerStyle}>
                          ANALYSIS CATEGORY
                        </TableCell>
                        <TableCell
                          className={classes.headerStyle}
                          sx={{ marginRight: '10px' }}
                        >
                          NAME
                        </TableCell>
                        {company &&
                          company.dates.map((header) => (
                            <TableCell
                              className={classes.headerStyle}
                              sx={{
                                width: '200px !important',
                                marginLeft: '10px',
                              }}
                              key={header}
                            >
                              {header}
                            </TableCell>
                          ))}
                      </th>
                    </TableRow>
                  </TableHead>
                  <TableBody>
             
                    {lineItems.map((lineItem, i) => (
                      <TableRow key={lineItem.id}>
                        <LineItemRow
                          i={i}
                          id={lineItem.id}
                          reportName={reportName}
                          level={lineItem.level}
                        />
                      </TableRow>
                    ))}
             
                  </TableBody>
                </Table>
              </Scrollbar>
            </CardContent>
          </Card>
        </Grid>
      </Grid>
    </div>
  );
};

CodePudding user response:

According to MDN docs, querySelector either takes an element to look for:

querySelector('plTable')
/* Looking for html tag plTable */

or an identifier:

querySelector('#plTable')
/* Looking for an element with id of plTable */

CodePudding user response:

Don't use DOM selectors in React, use refs to access DOM nodes. You can create a ref with useRef or React.createRef or you can pass a callback to the ref attribute of an element, that will receive the DOM node reference when the virtual DOM has done with the reconciliation.

To check if the node is mounted and do something with it being sure it is mounted, try this:

 <div
      id="plTable" // ****** Defined here *******
      style={{
        display: 'flex',
        alignItems: 'flex-end',
        margin: 'auto 0px',
      }}
      ref={node => {
        if (node) console.log("p1Table", node)
        //Do something with node
      }}
 >

  • Related