Uncaught TypeError: Cannot read properties of undefined (reading 'length') in my react app

Viewed 1144

I'm developing a react app but I am facing an uncaught type error. I've followed an online tutorial but I can't fix it. Here's my code:

import React, { useState, useEffect, createRef } from 'react';
import { CircularProgress, Grid, Typography, InputLabel, MenuItem, FormControl, Select } from '@material-ui/core';

import PlaceDetails from '../PlaceDetails/PlaceDetails';
import useStyles from './styles.js';
const List = ({ places, type, setType, rating, setRating, childClicked, isLoading }) => {
  const [elRefs, setElRefs] = useState([]);
  const classes = useStyles();

  useEffect(() => {
    setElRefs((refs) => Array(places.length).fill().map((_, i) => refs[i] || createRef()));
  }, [places]);

  return (
    <div className={classes.container}>
      <Typography variant="h4">Food & Dining around you</Typography>
      {isLoading ? (
        <div className={classes.loading}>
          <CircularProgress size="5rem" />
        </div>
      ) : (
        <>
          <FormControl className={classes.formControl}>
            <InputLabel id="type">Type</InputLabel>
            <Select id="type" value={type} onChange={(e) => setType(e.target.value)}>
              <MenuItem value="restaurants">Restaurants</MenuItem>
              <MenuItem value="hotels">Hotels</MenuItem>
              <MenuItem value="attractions">Attractions</MenuItem>
            </Select>
          </FormControl>
          <FormControl className={classes.formControl}>
            <InputLabel id="rating">Rating</InputLabel>
            <Select id="rating" value={rating} onChange={(e) => setRating(e.target.value)}>
              <MenuItem value="">All</MenuItem>
              <MenuItem value="3">Above 3.0</MenuItem>
              <MenuItem value="4">Above 4.0</MenuItem>
              <MenuItem value="4.5">Above 4.5</MenuItem>
            </Select>
          </FormControl>
          <Grid container spacing={3} className={classes.list}>
            {places?.map((place, i) => (
              <Grid ref={elRefs[i]} key={i} item xs={12}>
                <PlaceDetails selected={Number(childClicked) === i} refProp={elRefs[i]} place={place} />
              </Grid>
            ))}
          </Grid>
        </>
      )}
    </div>
  );
};

export default List;

I created a reference for my places before using a circularProgress in online tutorial he faced error like me but when he used circularProgress he was able to fix it but I don't understand why my error wouldn't fix

1 Answers

It seems that the places prop is dynamic and sometimes undefined. You can provide some fallback values. Use the Optional Chaining Operator on the places.length access, and provide 0 as a fallback value if places is null or undefined.

useEffect(() => {
  setElRefs((refs) => Array(places?.length ?? 0)
    .fill()
    .map((_, i) => refs[i] || createRef())
  );
}, [places]);

You could additionally provide a default places prop value.

const List = ({
  places = [],
  type,
  setType,
  rating,
  setRating,
  childClicked,
  isLoading,
}) => {
  ...

A more common pattern is to use a React ref to store the array of refs instead of using local state. Example:

const elRefs = React.useRef([]);

elRefs.current = Array(places?.length ?? 0)
  .fill()
  .map((_, i) => elRefs.current[i] || createRef());

...

{places?.map((place, i) => (
  <Grid ref={elRefs[i]} key={i} item xs={12}>
    <PlaceDetails
      selected={Number(childClicked) === i}
      refProp={elRefs.current[i]}
      place={place}
    />
  </Grid>
))}

This provides more stable ref references since all new refs won't be created when the places prop updates.

Related