Search code examples
axiosreact-hooksuse-effect

React - api call returns undefineds


const [loading, setLoading] = useState(false);
  const [meal, setMeal] = useState([]);

  useEffect(() => {
    setLoading(true);
    async function fetchData() {
      const response = await axios.get('/random.php');
      setMeal(response.data.meals);
      setLoading(false);
      return response;
    }

    fetchData();
  }, []);

  return (
    <Card className={classes.Root}>
      <CardActionArea>
        <CardMedia className={classes.Media} title="food">
          <img src={meal[0].strMealThumb} alt="cat" />
        </CardMedia>
        <CardContent>
          <Typography gutterBottom variant="h5" component="h2">
            {meal[0].strMeal}
          </Typography>
          <Typography variant="body2" color="textSecondary" component="p">
            {meal[0].strInstructions}
          </Typography>
        </CardContent>
      </CardActionArea>
      <CardActions>
        <Button size="small" color="primary">
          Learn More
        </Button>
      </CardActions>
    </Card>
  );

I'm using the coed above but I get an error TypeError: Cannot read property 'strMealThumb' of undefined. I tried multiple ways with useEffect and still have the same issue


Solution

  • When your component renders for the first time, the network request to /random.php hasn't happened yet, so meal is set to its default value ([]). You're attempting to set the image source to meal[0].strMealThumb, but meal is an empty array, so meal[0] returns undefined, and you see the error you posted.

    The solution is to prevent the component body from rendering while meal is loading, and when meal is an empty array (in case /random.php returns an empty array).

    meal is an array, so you should name it meals instead.

    const MyComponent = () => {
      const [loading, setLoading] = useState(false);
      const [meals, setMeals] = useState([]);
    
      if (loading) {
        return (
          <div>Loading...</div>
        );
      }
    
      if (meals.length === 0) {
        return (
          <div>No meals found!</div>
        );
      }
    
      return ( /* ... */ );
    };