Reputation: 75
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
Reputation: 203542
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>
);
}
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
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
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