I have a list of data users that I pass to another component which fetch the image of each user by mapping over the list.
import { useState, useRef, memo } from 'react'
const NewGoalsCon = memo(props => {
return (
<>
{props.data.map((el) => {
// get the user's avatar
async function setAvatar () {
const url = "http://localhost:3000/api/profile/images"
const r = await axios.get(url, {
headers: {
"user-id": el.user._id,
"extension": el.user.avatar.extension
}
})
return `data:image/${el.user.avatar.extension};base64, ${r.data}`
}
var avatar
if (el.user.avatar) {
setAvatar().then(res => avatar = res).catch(err => console.log(err))
} else {
avatar = avatarPlaceholder
}
console.log(avatar)
// check if the user has an avatar before you set the avatar constant
return (
<>
<div className='post-head-info'>
<div style={{ display: 'flex', alignItems: 'center' }}>
<img src={avatar} alt='' style={{ width: '60px', border: '2px solid #E8E8E8', borderRadius: '12px' }}/>
<div style={{ marginLeft: '10px' }}>
<h1 style={{ fontFamily: 'Open Sans Medium', fontSize: '20px', marginBottom: '5px' }}>{capitalize(el.user.fName) + ' ' + capitalize(el.user.lName)}</h1>
<p style={{ fontSize: '11.5px', color: '#999999' }}>{getMonth(el.date.split(",")[1].trim()) + " " + el.date.split(",")[0] + ", " + el.date.split(",")[2]}</p>
</div>
</div>
</>)})}
</>)
})
export default NewGoalsCon
My problem is that the avatar keeps re-rendering when I use useState, and I'm trying to set avatar to data:image/${el.user.avatar.extension};base64, ${r.data} where r.data is the base64. I also tried using a global variable but I can't assign it r.data. Any suggestions!
The reason why the avatar keeps re-rendering when you use useState is because React will re-render the component whenever the state changes, and in your current code calls setAvatar on each render, re-rendering the avatars each time. To fix this issue, you should cache the avatars using useState and use useEffect to only render the avatars once:
import { useState, useRef, memo } from 'react';
const NewGoalsCon = memo((props) => {
async function setAvatar(el) {
const url = 'http://localhost:3000/api/profile/images';
const r = await axios.get(url, {
headers: {
'user-id': el.user._id,
extension: el.user.avatar.extension,
},
});
return `data:image/${el.user.avatar.extension};base64, ${r.data}`;
}
const [avatars, setAvatars] = useState([])
useEffect(() => {
(async () => {
const renderedAvatars = []
for (const el of props.data) {
if (el.user.avatar) {
renderedAvatars.push(await setAvatar(el))
} else {
renderedAvatars.push(avatarPlaceholder)
}
}
setAvatars(renderedAvatars)
})()
}, [])
return (
<>
{props.data.map((el, i) => {
// check if the user has an avatar before you set the avatar constant
return (
<>
<div className="post-head-info">
<div style={{ display: 'flex', alignItems: 'center' }}>
<img
src={avatars[i]}
alt=""
style={{
width: '60px',
border: '2px solid #E8E8E8',
borderRadius: '12px',
}}
/>
<div style={{ marginLeft: '10px' }}>
<h1
style={{
fontFamily: 'Open Sans Medium',
fontSize: '20px',
marginBottom: '5px',
}}
>
{capitalize(el.user.fName) +
' ' +
capitalize(el.user.lName)}
</h1>
<p style={{ fontSize: '11.5px', color: '#999999' }}>
{getMonth(el.date.split(',')[1].trim()) +
' ' +
el.date.split(',')[0] +
', ' +
el.date.split(',')[2]}
</p>
</div>
</div>
</div>
</>
);
})}
</>
);
});
export default NewGoalsCon;
You can find more detailed information at this post I wrote (including other tips for your code): https://dialect.so/blog/fetching-an-image-from-a-server-within-map-in-react