Sachidananda MG
Sachidananda MG

Reputation: 75

Not able to render image from API

I am able to fetch the data from API endpoint but am not able to render it to the screen.

This is my App.js component.

    function App() {
  const [data, setData] = useState([]);
      const [img, setImg] = useState([]);

    

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

  async function fetchImage(name) {
    const requestOptions = {
      method: "GET",
      redirect: "follow",
    };
   await fetch(
      `https://avatars.dicebear.com/v2/avataaars/${name}.svg?options[mood][]=angry`,
      requestOptions
    )
      .then((response) => response.text())
      // .then((text)=>setImg(text))
      .catch((error) => console.log("error", error));
  }
  return (
    <div className="App">

      {data.map((details) => {
        return (
          <Card
            key={details.id}
            email={details.email}
            name={details.name}
            image={fetchImage(details.username)}
            phone={details.phone}
            website={details.website}
            company={details.company.name}
          />
        );
      })}
    </div>
  );
}

This is my card component.

    export default function Card(props) {
  return (
    <Box
      w="70vw"
      h="250px"
      borderWidth="1px"
      borderColor={"black"}
      overflow="hidden"
      m="2vh auto"
      display={"flex"}
      flexDir="row"
      color="black"
    >
      <Box>
        <Image
          w="20vw"
          h="250px"
          src={props.image}
          bg="gray"
        />
      </Box>

      <Box display='flex' flexDirection={'column'} alignItems='flex-start' ml='2vw' fontSize={'lg'}>
          <Box fontSize={'3xl'} mb='2vw'>{props.name}</Box>
          <Box><strong>Email: </strong>{props.email}</Box>
          <Box><strong>Phone: </strong>{props.phone}</Box>
          <Box><strong>Company: </strong>{props.company}</Box>
          <Box><strong>Website: </strong>{props.website}</Box>
          <Box><strong>Address: </strong>{props.address}</Box>
      </Box>
    </Box>

I figured out is returning promise instead of fetched data. If I uncomment the setImg promise, images are fetched continuously but still not rendering. Please help. Thanks in advance.

Upvotes: 1

Views: 912

Answers (3)

Drew Reese
Drew Reese

Reputation: 203542

Issue

The issue is that the fetchData function is unconditionally called when rendering the data array. When .then((text)=>setImg(text)) is uncommented it updates state and triggers a rerender. Repeat ad nauseam.

function App() {
  const [data, setData] = useState([]);
  const [img, setImg] = useState([]);

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

  async function fetchImage(name) {
    const requestOptions = {
      method: "GET",
      redirect: "follow",
    };
    await fetch(
      `https://avatars.dicebear.com/v2/avataaars/${name}.svg?options[mood][]=angry`,
      requestOptions
    )
      .then((response) => response.text())
      .then((text)=>setImg(text)) // <-- (2) update state, trigger rerender
      .catch((error) => console.log("error", error));
  }

  return (
    <div className="App">
      {data.map((details) => {
        return (
          <Card
            key={details.id}
            email={details.email}
            name={details.name}
            image={fetchImage(details.username)} // <-- (1) immediately invoked!
            phone={details.phone}
            website={details.website}
            company={details.company.name}
          />
        );
      })}
    </div>
  );
}

Solution

It doesn't appear you need to "fetch" the images, you can simply use a computed image source value. Create a utility function if you want.

function App() {
  const [data, setData] = useState([]);
  const [img, setImg] = useState([]);

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

  // (1) utility function to compute image source string
  const getImageSrc = name => {
    return `https://avatars.dicebear.com/v2/avataaars/${name}.svg?options[mood][]=angry`
  };

  return (
    <div className="App">
      {data.map((details) => {
        return (
          <Card
            key={details.id}
            email={details.email}
            name={details.name}
            image={getImageSrc(details.name)} // <-- (2) call utility
            phone={details.phone}
            website={details.website}
            company={details.company.name}
          />
        );
      })}
    </div>
  );
}

Upvotes: 1

VMT
VMT

Reputation: 819

App.js:

function App() {
  const [data, setData] = useState("");

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

  return (
    <div className="App">

      {data.map((details) => {
        return (
          <Card
            key={details.id}
            email={details.email}
            name={details.name}
            username={details.username} // <=== Change image prop to username prop
            phone={details.phone}
            website={details.website}
            company={details.company.name}
          />
        );
      })}
    </div>
  );
}

Card component:

export default function Card(props) {
  const imgURL = `https://avatars.dicebear.com/v2/avataaars/${props?.name}.svg?options[mood][]=angry`
  return (
    <Box
      w="70vw"
      h="250px"
      borderWidth="1px"
      borderColor={"black"}
      overflow="hidden"
      m="2vh auto"
      display={"flex"}
      flexDir="row"
      color="black"
    >
      <Box>
        <Image
          w="20vw"
          h="250px"
          src={imgURL} //<=== This is src img
          bg="gray"
        />
      </Box>

      <Box display='flex' flexDirection={'column'} alignItems='flex-start' ml='2vw' fontSize={'lg'}>
          <Box fontSize={'3xl'} mb='2vw'>{props.name}</Box>
          <Box><strong>Email: </strong>{props.email}</Box>
          <Box><strong>Phone: </strong>{props.phone}</Box>
          <Box><strong>Company: </strong>{props.company}</Box>
          <Box><strong>Website: </strong>{props.website}</Box>
          <Box><strong>Address: </strong>{props.address}</Box>
      </Box>
    </Box>

Upvotes: 1

Maneal
Maneal

Reputation: 169

You'r not using the await fetch(...) result

In general, don't mix await and then use one or another

Plus, if api returns image data, you should not convert it to text, but to blob and use URL.createObjectURL to create a correct url

const response = await fetch(
      `https://avatars.dicebear.com/v2/avataaars/${name}.svg?options[mood][]=angry`,
      requestOptions
    )
const blob = await response.blob();
return URL.createObjectURL(blob)

Or use the api url directly if you can:

return `https://avatars.dicebear.com/v2/avataaars/${name}.svg?options[mood][]=angry`;

But you should use state to store the generated URLs. This code could be used in your fetchData() function and add the imageUrl to each item to not refetch on each render

Upvotes: 1

Related Questions