I recently incorporated displaying images along with quotes in my web app. Unfortunately, when I stopped to take a closer look at the front end, I was seeing images re-render multiple times because of how react handles rendering content. (If you look closely, you can see images changing seemingly in place)

For context, as my image source, I downloaded nature images from pixabay and put them in a public/images folder. I was randomly choosing what image would be associated with quote on render, which meant that the images would change during every single render (see code below).
<Card style={{ width: '24rem' }}>
<Card.Img variant="top" src={imgSrc[mapped[Math.floor(Math.random() * imgSrc.length)]]} />
<Card.Body>
<Card.Title>{index+1}. "{quote}"</Card.Title>
<Card.Text>~ {author}</Card.Text>
</Card.Body>
</Card>
I wasn’t really sure how to sort through debugging this re-render. At first, I dug into using useCallback and React.memo to control the re-rendering, but I couldn’t quite wrap my head around using those for my own use case.
When that didn’t seem to work out, I decided to map a quote id to a random image and refer to that definition when choosing what image to display with a quote (instead of randomly assigning a quote on render).
useEffect(() => {
if (Object.keys(mapped).length === 0) {
if (quotes.length > 0) {
let quoteToImgMap = {};
for (let q in quotes) {
let idx = Math.floor(Math.random() * imgSrc.length);
quoteToImgMap = {...quoteToImgMap, ...{[quotes[q].id]:idx}};
}
setMapped(quoteToImgMap);
}
}
}, [quotes, mapped]);
The above is what I came up with. If the mapped object isn’t populated, I check to make sure the length of quotes is greater than 0 before diving in to map the quotes to an image index. Lastly I set the mapped state variable (not listed above) to my updated map, capturing the list of quotes from the server and the mapped state variable as my dependencies.
I won’t show the result, but because of this update, my images don’t change on every render anymore!