skip to Main Content

My Images.js is as follows:

import { useState, useEffect } from "react"; 
import axios from "axios"; 
import styled from "styled-components";
import ImageSlider_1 from "./ImageSlider_1";

const url = "https://hp-api.onrender.com/api/characters";

function Images() {   
  const [isLoading, setIsLoading] = useState(true);   
  const [isError, setIsError] = useState(false);   
  const [characters, setCharacters] = useState([]);

  useEffect(() => {
    const fetchCharacters = async () => {
      try {
        const response = await axios(url);
        const data = await response.data;

        data.map((character) => {
          if (character.image.localeCompare("")) {
            setCharacters((characters) => [...characters, character]);
          }
        });
      } catch (error) {
        setIsError(true);
        console.log(error);
      }
      setIsLoading(false);
    };
    fetchCharacters();   }, []);

  return (
    <Wrapper>
      <ImageSlider_1 characters={characters} />
      {/* <Carousel_Swiper characters={characters} /> */}
    </Wrapper>   ); }

const Wrapper = styled.section``;

export default Images;

My ImageSldier_1.js is as follows:

import { useState } from "react";
import styled from "styled-components";

function ImageSlider_1({ characters }) {
  console.log(characters[0].image);

  // const [sliderData, setSliderData] = useState(characters[0].image);
    
  return (
    <Wrapper>
      {characters.map((character) => {
        return (
          <img src={character.image} alt={character.name} key={character.id} />
        );
      })}
    </Wrapper>
  );
}

const Wrapper = styled.section``;

export default ImageSlider_1;

I am not sure why characters[0].image gives me error as this:

Uncaught TypeError: Cannot read properties of undefined (reading
‘image’)

However, when I do console.log(characters); instead, it shows me there are 25 items.

I can do characters.map((character) => console.log(character.image)); to display each of the image link, but then I need to set first image in the state variable:

const [sliderData, setSliderData] = useState(characters[0].image);

I am not sure what I am doing wrong.

2

Answers


  1. I can do: characters.map((character) => console.log(character.image));

    You can use the Array#map function since `characters is an array.

    I am not sure why characters[0].image gives me error as this:

    If characters is an empty array ([]), the first element of it ([0]) will be equal to undefined. And if it’s undefined, trying to access image property (.image) will result in an error, since undefined does not have such property.

    You could use optional chaining (?) to secure if characters[0] is undefined:

    console.log(characters[0]?.image);
    

    To set the state value, you should add useEffect to listen to characters changes and if it’s populated, set the image.

    const [sliderData, setSliderData] = useState(null);
    
    useEffect(() => {
       setSliderData(characters[0]?.image ?? null);
    }, [characters]); 
    
    Login or Signup to reply.
  2. characters is initially an empty array in the parent Images component:

    const [characters, setCharacters] = useState([]);
    

    So on the initial render cycle the value [] is passed to ImageSlider_1 as a prop value until the useEffect hook in Images runs and fetches and updates the characters state and rerenders itself and its sub-ReactTree. There’s no zero’th element to access an image property of and the error is thrown.

    Now while you could store the passed characters[0].image into local state, using a useEffect hook to synchronize it, this is actually considered a React anti-pattern. Don’t store the passed prop values in local state, and don’t store derived state in local state. Just reference the passed prop value directly and compute the derived state.

    function ImageSlider_1({ characters = [] }) {
      const sliderData = characters[0]?.image;
        
      return (
        <Wrapper>
          {characters.map((character) => {
            return (
              <img src={character.image} alt={character.name} key={character.id} />
            );
          })}
        </Wrapper>
      );
    }
    

    Just about 100% of the time if you catch that you’ve coded a useState/useEffect combo you should really use the useMemo hook.

    Instead of

    function ImageSlider_1({ characters = [] }) {
      const [sliderData, setSliderData] = useState(characters[0]?.image);
    
      useEffect(() => {
        setSliderData(characters[0]?.image);
      }, [characters]);
        
      return (
        ...
      );
    }
    

    use

    function ImageSlider_1({ characters = [] }) {
      const sliderData = useMemo(() => characters[0]?.image, [characters]);
        
      return (
        ...
      );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search