Robimy to, co w lekcji poprzedniej, ale tym razem używając frameworka React i jego hooków – useState i useEffect. Do dzieła.

Ok, przypomnijmy sobie, co robi useEffect z pustą tablicą zależności:

 useEffect(() => {
        fetch("https://pokeapi.co/api/v2/type/3")
    }, []);

Z pustą – działa jeden raz, po podłączeniu. Ok, a coś takiego jak cleanup function pamiętamy?

Składnia taka:

import React, { useEffect } from "React";

useEffect(() => {
  // Your effect
  return () => {
    // Cleanup
  };
}, []);

Czyli w returnie useEffect podajemy funkcję, która jest funkcją cleanup. Przykład z abortcontrollerem:

useEffect(() => {
  //create the abort controller
  let controller = new AbortController();

  (async () => {
    try {
      const response = await fetch(APIEndpoint, {
        // attach the controller to the request
        signal: controller.signal,
      });
      // add the success response to a state value
    } catch (e) {
      // Handle the error
    }
  })();
  //abort the request when the component umounts
  return () => controller?.abort();
}, []);

Ok, abort controllera znamy z eventów, ale dla różnych fetchów też go stosować można.

Przykład cleanupu z timeout:

useEffect(() => {
  let timerId = setTimeout(() => {
    // perform an action like state update
    timerId = null;
  }, 5000);

  // clear the timer when component unmouts
  return () => clearTimeout(timerId);
}, []);

Do takich rzeczy używamy funkcji cleanup. Ok, mam nadzieję, że pamiętamy też o useState oraz że każde wywołanie setState triggeruje re-render.

Dobra, zaczynamy, przykład z dokumentacji Reacta btw:

import { useState, useEffect } from 'react';

export default function App() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

Na razie chyba rozumiemy. Initial state na 0 dla x,y, position to ten stan (do czytania), setPosition ustawia nowy stan i triggeruje re-render.

To teraz useEffect:

import { useState, useEffect } from 'react';

export default function App() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    function handleMove(e) {
      setPosition({ x: e.clientX, y: e.clientY });
    }
    window.addEventListener('pointermove', handleMove);
    return () => {
      window.removeEventListener('pointermove', handleMove);
    };
  }, []);

Jest tak:

  • pusta tablica zależności, więc useEffect rusza jeden raz
  • funcja handleMove używa setPosition więc wymusza re-render
  • useEffect nadaje się do takich zastosowań, jak globalne eventy przeglądarki i inne efekty
  • jak dodaliśmy event listener, to funkcja cleanup go musi posprzątać

No dobra, a gdzie będzie ten nasz dot? Cóż, tym się zajmie zwracany JSX:

import { useState, useEffect } from 'react';

export default function App() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    function handleMove(e) {
      setPosition({ x: e.clientX, y: e.clientY });
    }
    window.addEventListener('pointermove', handleMove);
    return () => {
      window.removeEventListener('pointermove', handleMove);
    };
  }, []);

  return (
    <div style={{
      position: 'absolute',
      backgroundColor: 'pink',
      borderRadius: '50%',
      opacity: 0.6,
      transform: `translate(${position.x}px, ${position.y}px)`,
      pointerEvents: 'none',
      left: -20,
      top: -20,
      width: 40,
      height: 40,
    }} />
  );
}

Jak widać myślniki zastąpione wielką literą, uchodzi brak jednostek, property values jako string (chyba że numeryczny typ bez jednostki), daje radę użyć interpolacji do naszego transforma.

Więcej Reacta niedługo.