Red Ochsenbein

React: Switch background images when they are ready

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.

© 2021 – 2023 Red Ochsenbein