Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NextJS: Images loaded from cache don't trigger the onLoad event

I am currently in the process of converting a ReactJS client side rendered application to a NextJS application for search engine optimization and social media linking reasons.

One of the components converted, which is basically an image that waits until it's finished loading then fades in, is not working as expected after it is used in a NextJS environment.

It behaves in the following manner:

Cache Enabled:

  • The first time the image loads and the onLoad event triggers thus showing the image.
  • The second time the image stays hidden because the onLoad event doesn't trigger when the image is loaded from cache.

Cache Disabled using devtools:

  • The onLoad event always works because the image is never served from cache.

Expected behavior and the behavior previously achieved with using just ReactJS:

  • The onLoad event should trigger whether the image was loaded from the cache or not.

When not using React this problem is usually caused when someone sets the images src before defining a onload function:

let img = new Image()
img.src = "img.jpg"
img.onload = () => console.log("Image loaded.")

Which should be:

let img = new Image()
img.onload = () => console.log("Image loaded.")
img.src = "img.jpg"

Here is simplified code that causes the same problem in NextJS:

import React, { useState } from "react"

const Home = () => {
    const [loaded, setLoaded] = useState(false)

    const homeStyles = {
        width: "100%",
        height: "96vh",
        backgroundColor: "black"
    }

    const imgStyles = {
        width: "100%",
        height: "100%",
        objectFit: "cover",
        opacity: loaded ? 1 : 0
    }

    const handleLoad = () => {
        console.log("Loaded")
        setLoaded(true)
    }

    return (
        <div className="Home" style={homeStyles}>
            <img alt=""
                onLoad={handleLoad}
                src="https://images.unsplash.com/photo-1558981001-5864b3250a69?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80"
                style={imgStyles}
            />
        </div>
    )
}

export default Home
like image 656
Mohamed Seif Khalid Avatar asked Jan 17 '20 12:01

Mohamed Seif Khalid


1 Answers

I ended up using ImageObject.complete as a workaround thanks to someone's suggestion.

I used useRef to reference the image and checked if the image.current.complete === true on component mount.

Here is the code:

import React, { useEffect, useRef, useState } from "react"

const Home = () => {
    const [loaded, setLoaded] = useState(false)

    const image = useRef()

    const homeStyles = {
        width: "100%",
        height: "96vh",
        backgroundColor: "black"
    }

    const imgStyles = {
        width: "100%",
        height: "100%",
        objectFit: "cover",
        opacity: loaded ? 1 : 0
    }

    const handleLoad = () => setLoaded(true)

    useEffect(() => {
        if (image.current.complete) setLoaded(true)
    }, [])

    return (
        <div className="Home" style={homeStyles}>
            <img alt=""
                ref={image}
                onLoad={handleLoad}
                src="https://images.unsplash.com/photo-1558981001-5864b3250a69?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80"
                style={imgStyles}
            />
        </div>
    )
}

export default Home
like image 98
Mohamed Seif Khalid Avatar answered Sep 24 '22 20:09

Mohamed Seif Khalid