React: Switch background images when they are ready
Read and comment on Dev.to
In an application I’m currently working on I needed a way to make sure the image is actually loaded before I put it into the background. How did I do that?
Actaully it’s pretty simple: Create an image object, load the image - which then would be put into the cache - and when the image object sends the load event switch the image.
import React, { PropsWithChildren, useCallback, useState } from "react";
import styled from "styled-components";
import useWsMessage from "../hooks/useWsMessage";
interface BackgroundData {
credits?: string;
url: string;
}
const Background: React.FC<PropsWithChildren> = ({ children }) => {
const [current, setCurrent] = useState<BackgroundData | null>(null);
const handleMesssage = useCallback((event: MessageEvent<string>) => {
const raw = event.data.replace("SetBackground ", "");
try {
const background: BackgroundData = JSON.parse(raw);
const img = new Image();
img.src = background.url;
img.onload = () => setCurrent(background);
} catch {
// do nothing
}
}, []);
useWsMessage("SetBackground", handleMesssage);
return <BackgroundRenderer url={current?.url}>{children}</BackgroundRenderer>;
};
interface BackgroundRendererProps {
url?: string;
}
const BackgroundRenderer = styled.div.attrs<BackgroundRendererProps>(
({ url }) => {
return {
style: {
backgroundColor: "black",
...(url ? { backgroundImage: `url(${url})` } : {}),
},
};
},
)<BackgroundRendererProps>`
background-position: center center;
background-size: cover;
background-repeat: no-repeat;
bottom: 0;
height: 100%;
left: 0;
position: fixed;
right: 0;
top: 0;
width: 100%;
`;
export default Background;
As you can see the component retrieves a message from websocket and uses the URL to set the background image. The important part is this:
const img = new Image();
img.src = background.url;
img.onload = () => setCurrent(background);
It creates an image object, sets the src and sets the background image state as soon as the image is loaded.
That’s it. Pretty simple, huh? I hope this helps someone having a similar problem in the future.