Nero
Nero

Reputation: 1575

Reactjs how to lazy load image after DOM is loaded

I want to make a reactjs page lazy loading images. Therefore I call lazyLoadImages() inside the componentDidMount(). I found that the browser loading indicator located at browser tab still keep spinning all the way until all images are loaded. This make me think what I've done does not provide a true lazy load image experience.

The interesting thing is that if I wrap lazyLoadImages() in setTimeout with a not too short timeout argument, such as 100ms (not 0 or even 50ms), then the page loading indicator will very soon stop spinning and disappear, which gives me a feeling that the page DOM is complete while those images are started to load at a background process.

I thought the componentDidMount and window onload are something similar, but they are not. I can have the same experience as using setTimeout by using the window onload event. But since my application is a SPA, therefore onload event is not suitable. Because onload event only work when I explicitly refresh this page, but not navigating between pages using react-router.

Anyone has idea on this phenomenon and how can I achieve the same without using setTimeout function?

componentDidMount() {
    setTimeout(this.lazyLoadCarouselImage, 100);
    // or invoke directly -> this.lazyLoadCarouselImage();
}
    
lazyLoadCarouselImage() {
   let images = [];
   let loadedCounter = 0;
   let lazyLoadImageList = ['https://wallpaperaccess.com/full/637960.jpg','https://wallpaperaccess.com/full/637960.jpg']

   for (let i=0; i<lazyLoadImageList.length; i++ ) {
      images[i] = new Image();
      images[i].onload = ()=> {
                    loadedCounter++;
                    if (loadedCounter == lazyLoadImageList.length) {
                        // update state here saying all images are loaded
                    }
                }
      images[i].src = lazyLoadImageList[i];
   }
}

Upvotes: 1

Views: 1738

Answers (2)

Reza Babaei
Reza Babaei

Reputation: 1075

seems it's working to me

const [isLoaded, setIsLoaded] = useState(false);
const [isPageLoaded, setIsPageLoaded] = useState(false); //this helps with lazy loading

useEffect(() => {
    setIsLoaded(true);
}, []);

useEffect(() => {
    if (isLoaded) {
        setIsPageLoaded(true);
    }
}, [isLoaded]);

Upvotes: 0

Juan Marco
Juan Marco

Reputation: 3241

There are libraries that can handle the lazy loading using scroll and resize event handlers, while others use Intersection Observer API. Check out Lazy-loading images for details.

Nowadays you can lazy load images simply by adding the attribute loading="lazy" to each <img> element. Just keep in mind that the feature is fairly new, so make sure potential users are using an up to date Browser.

Below is a quick example where I create 100 images that are "lazy loaded":

class App extends React.Component {



  render() {

    let results = [];

    for (let i = 0; i < 100; i++) {

        results.push(
          <img
            key={`image${i}`}
            src={`https://placehold.it/4${i < 10 ? `0${i}` : i }x4${i < 10 ? `0${i}` : i }`}
            alt="placeholder"
            width={`4${i < 10 ? `0${i}` : i }`}
            height={`4${i < 10 ? `0${i}` : i }`}
            loading="lazy"
          />
        )

    }

      return (
        <div style={{ width: '400px' }}>
          {results}
        </div>
      )

    }

  }



  ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Note: Click "Run code snippet", then open the "Network" tab inside your Browser's developer tools, begin to scroll and you should notice how the images load as they become close to the viewport.

Upvotes: 1

Related Questions