Na przykładzie kodu znalezionego w internecie poznajemy jak działa useEffect razem z asynchronicznymi funkcjami. Do dzieła.

Myślę, że najlepiej będzie to przedstawić kawałkami:

export default function App() {
  const [amount, setAmount] = useState(1);
  
  //(...)

  useEffect(
    //(...)
    [amount, fromCur, toCur]
  );

  return (
    <div>
      <input
        type="text"
        value={amount}
        onChange={(e) => setAmount(Number(e.target.value))}
        disabled={isLoading}
      />
//(...)

Czyli tak:

  • Pod App mamy zmienne stanowe tego RFC
  • W useEffect będzie definicja funkcji async robiącej await na fetch api i wywołanie tej funkcji
  • W Returnie mamy JSX

Na razie widzimy amount i odpowiadający mu input. Typu tekstowego, zatem e.target.value musimy konwertować do number. Disabled bindowane do zmiennej isLoading, na razie jej nie widzimy.

Pozostałe zmienne powiązane z markupem JSX:

const [fromCur, setFromCur] = useState("EUR");
const [toCur, setToCur] = useState("USD");
const [converted, setConverted] = useState("");
const [isLoading, setIsLoading] = useState(false);

//(...)

<select
        value={fromCur}
        onChange={(e) => setFromCur(e.target.value)}
        disabled={isLoading}
      >
        <option value="USD">USD</option>
        <option value="EUR">EUR</option>
        <option value="CAD">CAD</option>
        <option value="INR">INR</option>
      </select>
      <select
        value={toCur}
        onChange={(e) => setToCur(e.target.value)}
        disabled={isLoading}
      >
        <option value="USD">USD</option>
        <option value="EUR">EUR</option>
        <option value="CAD">CAD</option>
        <option value="INR">INR</option>
      </select>
      <p>
        {converted} {toCur}
      </p>
    </div>
  );
}

Jak widać nigdzie nie mamy ustawienia setIsLoading ani setConverted, te nasze inputy zmieniają tylko:

  • Jaką liczbę konwertujemy
  • Z jakiej waluty
  • Na jaką walutę

Ustawienie loading na true oraz sama konwersja musi zatem odbywać się w useEffect. Tak ono wygląda:

 useEffect(
    function () {
      async function convert() {
        setIsLoading(true);
        const res = await fetch(
          `https://api.frankfurter.app/latest?amount=${amount}&from=${fromCur}&to=${toCur}`
        );
        const data = await res.json();
        setConverted(data.rates[toCur]);
        setIsLoading(false);
      }

      if (fromCur === toCur) return setConverted(amount);
      convert();
    },
    [amount, fromCur, toCur]
  );

Mamy w callbacku:

  • Definicję async funkcji convert
  • Ta funkcja ustawia isLoading na true
  • Następnie robi res, na którym jest await fetch
  • Następnie robi await na res.json (nowa składnia, kiedyś mieliśmy promise, then i tak dalej)
  • Ustawia konwersję na to, co przyszło z api
  • Ustawia ładowanie na false
  • Poniżej definicji mamy sprawdzenie czy aby waluty nie są takie same i return plus ustawienie converted na to samo (np. 5 dolarów to… 5 dolarów) bez opóźnienia
  • Jeżeli różnią się to wywołujemy funkcję convert, która jest asynchroniczna
  • W zależnościach mamy amount, fromCur, toCur, nie wybieramy sobie zależności, to warto podkreślić, ale efekt chodzi gdy któraś się zmieni

Więcej Reacta niedługo…