useLayoutEffect: – useEffecten du ikke visste fantes!

– Den irriterende layout-shiften ble løst av en perle fra Reacts verktøykasse som jeg aldri hadde hørt om, skriver Marcus Haaland.

Marcus Haaland er utvikler i Bekk. 📸: Bekk / kode24
Marcus Haaland er utvikler i Bekk. 📸: Bekk / kode24 Vis mer

Nylig skulle jeg lage en innholdsfortegnelse-komponent. Da måtte jeg hente ut alle titlene som var på siden, og vise dem i en liste. useEffect funka, men det ga et irriterende layout-shift som jeg ikke ønsket.

Det som løste biffen var en perle fra Reacts verktøykasse jeg aldri hadde hørt om: useLayoutEffect.

«Neste gang du støter borti lugging i det visuelle, er det kanskje useLayoutEffect som er løsningen.»

Forskjellen på useEffect og useLayoutEffect

Akkurat som useEffect, blir useLayoutEffect brukt til å utføre sideeffekter i komponenter. Forskjellen ligger i tidspunktet for når de kjøres.

Mens useEffect utføres etter at komponenten er rendret (og dermed ikke blokkerer visuell oppdatering), trer useLayoutEffect i kraft før selve side-tegningen skjer.

La oss se på hva dette betyr:

Da jeg brukte useEffect, oppstod det layout-shifts i komponenten. Årsaken var at komponenten ble rendret en gang, så en gang til etter at listevisningen var beregnet. Siden useEffect opererer asynkront, vil den ikke stoppe resten av komponenten fra å vises mens disse beregningene pågår.

Her er et forenklet kodeeksempel, hvor det viktigste var at jeg brukte useEffect for å beregne en verdi for visningen av komponenten:

export function TableOfContent(): JSX.Element {
  const [headings, setHeadings] = useState<string[]>([]);

  useEffect(() => {
    const calculatedHeadings = getHeadings();
    setHeadings(calculatedHeadings);
  }, []);

  return (
    <nav>
      <h2>Innhold</h2>
      <ul>
        {headings.map((heading, i) => (
          <li key={i}>{heading}</li>
        ))}
      </ul>
    </nav>
  );
}

useLayoutEffect kjører før siden faktisk tegnes. Det betyr at den stopper opp visningen til alle beregninger er gjort, slik at hele innholdsfortegnelse-komponenten kan rendres på én gang, uten uønskede endringer i layout.

Her er samme kodeeksempel som over, bare at useEffect er byttet ut med useLayoutEffect:

export function TableOfContent(): JSX.Element {
  const [headings, setHeadings] = useState<string[]>([]);

  useLayoutEffect(() => {
    const calculatedHeadings = getHeadings();
    setHeadings(calculatedHeadings);
  }, []);

  return (
    <nav>
      <h2>Innhold</h2>
      <ul>
        {headings.map((heading, i) => (
          <li key={i}>{heading}</li>
        ))}
      </ul>
    </nav>
  );
}

useEffect kjøres altså etter at komponenten er rendret (og dermed ikke blokkerer visuell oppdatering), mens useLayoutEffect kjøres før selve side-tegningen skjer.

Når bør du bruke useLayoutEffect over useEffect?

Svaret ligger i grunn i navnet: Hvis du skal endre noe som kan påvirke layout, er dét et stikkord til å vurdere useLayoutEffect.

Men her er en advarsel: Du bør ikke bare kaste på useLayoutEffect overalt hvor du vanligvis ville brukt en useEffect. Siden useLayoutEffect blokkerer rendring, kan det føre til lengre lasting og en tregere brukeropplevelse.

Praktisk sett kan du starte med en useEffect. Om det visuelle lugger, kan du prøve å erstatte useEffect med en useLayoutEffect og se om det løser problemet.

Konklusjon

useLayoutEffect er en ofte oversett, men en utrolig kraftig hook i Reacts arsenal. Den gir deg kontroll over renderingsprosessen, som kan være avgjørende for både ytelse og brukeropplevelse.

Neste gang du støter borti lugging i det visuelle, er det kanskje nettopp useLayoutEffect som er løsningen.

Du kan lese mer om useLayoutEffect i Reacts dokumentasjon: https://react.dev/reference/react/useLayoutEffect