You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

React中如何仅加载可见图片?画廊应用API调用优化需求

Lazy Loading API Calls for Visible Thumbnails in React

Absolutely! This is a classic problem that’s perfectly solved with lazy loading paired with intersection observation—it’ll drastically cut down on unnecessary API calls and boost your app’s performance. Let’s break down the most practical approaches for your React gallery:

1. Native Intersection Observer API (No External Libraries)

The browser’s built-in Intersection Observer API is lightweight and efficient for detecting when elements enter the viewport. Here’s how to implement it in your thumbnail component:

import { useEffect, useRef, useState } from 'react';

const Thumbnail = ({ id }) => {
  const [imageData, setImageData] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  const thumbnailRef = useRef(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        const [entry] = entries;
        if (entry.isIntersecting && !imageData && !isLoading) {
          // Trigger API call when the thumbnail enters the viewport
          setIsLoading(true);
          fetch(`/api/thumbnails/${id}`)
            .then(res => res.json())
            .then(data => {
              setImageData(data);
              setIsLoading(false);
            })
            .catch(err => {
              console.error('Failed to load thumbnail:', err);
              setIsLoading(false);
            });
          // Disconnect observer once we've loaded the data
          observer.disconnect();
        }
      },
      { threshold: 0.1 } // Trigger when 10% of the element is visible
    );

    if (thumbnailRef.current) {
      observer.observe(thumbnailRef.current);
    }

    // Cleanup: Disconnect observer on component unmount
    return () => {
      if (observer) observer.disconnect();
    };
  }, [id, imageData, isLoading]);

  if (isLoading) return <div ref={thumbnailRef} className="thumbnail-placeholder">Loading...</div>;
  if (!imageData) return <div ref={thumbnailRef} className="thumbnail-placeholder"></div>;

  return (
    <div ref={thumbnailRef} className="thumbnail">
      <img src={imageData.thumbnailUrl} alt={imageData.title} />
    </div>
  );
};

// Then in your gallery component:
const Gallery = () => {
  const thumbnailIds = Array.from({ length: 100 }, (_, i) => i + 1);
  return (
    <div className="gallery-grid">
      {thumbnailIds.map(id => <Thumbnail key={id} id={id} />)}
    </div>
  );
};

2. Use the react-intersection-observer Library (Simpler React Integration)

If you want to reduce boilerplate code, the react-intersection-observer package wraps the native API into React hooks and components, making it even easier to use:

First install it:

npm install react-intersection-observer

Then implement the thumbnail component:

import { useState, useEffect } from 'react';
import { useInView } from 'react-intersection-observer';

const Thumbnail = ({ id }) => {
  const [imageData, setImageData] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  // Configure the observer: trigger when 10% visible, once only
  const { ref, inView } = useInView({ threshold: 0.1, triggerOnce: true });

  useEffect(() => {
    if (inView && !imageData && !isLoading) {
      setIsLoading(true);
      fetch(`/api/thumbnails/${id}`)
        .then(res => res.json())
        .then(data => {
          setImageData(data);
          setIsLoading(false);
        })
        .catch(err => {
          console.error('Failed to load thumbnail:', err);
          setIsLoading(false);
        });
    }
  }, [inView, id, imageData, isLoading]);

  if (isLoading) return <div ref={ref} className="thumbnail-placeholder">Loading...</div>;
  if (!imageData) return <div ref={ref} className="thumbnail-placeholder"></div>;

  return (
    <div ref={ref} className="thumbnail">
      <img src={imageData.thumbnailUrl} alt={imageData.title} />
    </div>
  );
};

Key Tips for Smooth Implementation

  • Add Placeholders: Use skeleton loaders or empty divs with the same dimensions as your thumbnails to prevent layout shifts when images load.
  • Handle Edge Cases: Make sure to catch API errors and handle loading states so users know what’s happening.
  • Preload Nearby Elements: Adjust the rootMargin option in Intersection Observer to start loading thumbnails just before they enter the viewport (e.g., rootMargin: '200px'), so users don’t see loading spinners as they scroll.
  • Avoid Overloading the Server: If users scroll extremely fast, you might add a small delay (e.g., 100ms) before triggering the API call—but Intersection Observer is already optimized for this scenario, so it’s often unnecessary.

This approach ensures you only call the API for thumbnails that are actually visible (or about to be visible) on the screen, instead of all 100 upfront. It’s a huge win for performance, especially on mobile devices or slow networks!

内容的提问来源于stack exchange,提问作者skyshine

火山引擎 最新活动