javascript - Vad är det korrekta sättet att skapa och återkalla en blob i React useEffect?


Överväg :

import { useEffect, useState, type ReactElement } from 'react';

async function getImage(): Promise<Blob> {
  // Some random async code
  const res = await fetch('https://picsum.photos/200');
  const blob = await res.blob();
  return blob;
}

export default function Image(): ReactElement {
  const [src, setSrc] = useState<string | null>(null);
  useEffect(() => {
    getImage()
      .then((blob) => {
        const url = URL.createObjectURL(blob);
        setSrc(url);
      })
      .catch((error) => {
        throw error;
      });

    return () => {
      if (src !== null) {
        URL.revokeObjectURL(src);
      }
    };
  }, [src]);
  return <img src={src ?? undefined} alt="some image" />;

Jag vill rendera en bild från en Blob och sedan återkalla den efter att komponenten avmonterats. Men detta verkar få effekten att köra i en oändlig loop. Vad är det rätta sättet att göra detta eller bästa praxis för att hantera det?

Svar #1

Den oändliga loopen i din kod orsakas av att inkludera src som ett beroende i useEffect-beroende arrayen. När värdet på src ändras kommer effekten att triggas igen, vilket resulterar i en oändlig loop.

För att åtgärda detta problem kan du ändra din kod på följande sätt:

import { useEffect, useState, type ReactElement } from 'react';

async function getImage(): Promise<Blob> {
  // some random async code
  const res = await fetch('https://picsum.photos/200');
  const blob = await res.blob();
  return blob;
}

export default function Image(): ReactElement {
  const [src, setSrc] = useState<string | null>(null);
  
  useEffect(() => {
    let url: string | null = null;
    
    getImage()
      .then((blob) => {
        url = URL.createObjectURL(blob);
        setSrc(url);
      })
      .catch((error) => {
        throw error;
      });

    return () => {
      if (url !== null) {
        URL.revokeObjectURL(url);
      }
    };
  }, []);
  
  return <img src={src ?? undefined} alt="some image" />;
}

I denna uppdaterade kod tog jag bort src från beroendearrayen för useEffect, vilket förhindrar att effekten triggas varje gång src ändras. Istället lade jag till en lokal variabel url inuti effekten för att lagra URL:en som skapats av URL.createObjectURL(). Rensningsfunktionen använder sedan variabeln url för att upphäva objekt-URL:en.

Observera att eftersom getImage är en asynkron funktion är det bättre att hantera eventuella fel inom effekten själv i stället för att använda catch och kasta om felet. Du kan lägga till en try/catch-block runt getImage-anropet för att hantera eventuella undantag på ett smidigt sätt.