I'm putting the finishing touches on my first React application. But one annoying problem: I need to cache thumbnails for the items I am displaying. So I'm using a useEffect which can be simply stated like this:
var thumbnailLookUp = {};
useEffect(() => {
fetch("http://localhost:3003/thumbnailPreload")
.then((response) => response.json())
.then((data) => {
for (var row of data) {
thumbnailLookUp[row.uuid] = row.thumb;
}
})
.then(countClips())
.catch((err) => {
console.log(err.message);
})
}, []);
The countClips function assembles the data list which will be rendered by a map() in the primary render. This includes the following, where CardMedia is a @mui custom component:
<CardMedia
component="img"
height="100"
src={`https://d3pe68qfxlhu0a.cloudfront.net/${
thumbnailLookUp[event.uuid]
}`}
/>
Well here's the thing. When I do a full reload of the page, half the time this works, and the other half the time I get undefined for the results of every thumbnailLookUp[event.uuid]. It's consistent: if one fails, they all fail, if one succeeds, they all succeed. This tells me there's some kind of a race condition between the effect and the render, which is not something I've ever read about. I can guarantee success by placing a breakpoint!
How can I tighten this up so that thumbnailLookUp is always populated before the render (or a second render, if that's really necessary)?
To make sure that that thumbnailLookUp is always populated before the render you should wait for the value to exist before actually rendering the CardMedia component.
You can do this by checking that by writing this:
{thumbnailLookUp && (
<CardMedia
component="img"
height="100"
src={`https://d3pe68qfxlhu0a.cloudfront.net/${
thumbnailLookUp[event.uuid]
}`}
/>
)}
But given your current implementation, you also need to keep that thumbnailLookUp in the component lifecycle state, and it should initializes as undefined so that nothing gets rendered immediately (since {} is truthy).
In the end you should have this:
function MyComponent() {
const [thumbnailLookUp, setThumbnailLookup] = useState(); // this initializes as undefined
useEffect(() => {
fetch(...)
...
.then((data) => {
const newLookup = {};
for (var row of data) {
newLookup[row.uuid] = row.thumb;
}
setThumbnailLookup(newLookup);
})
...
}, [])
return ...;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With