Poznajemy czym jest updater function i do czego może nam się przydać korzystanie z niej. Kontynuacja poprzednich lekcji, do dzieła.
W React mamy wiele powodów do korzystania z funkcji:
- RFC, komponent funkcyjny – funkcja zwracająca JSX i korzystająca z hooków na swoim najwyższym i niezagnieżdżonym poziomie
- Funkcja cleanup – używana w useEffect, przeciwieństwo setup, wiemy już jak z niej korzystać i po co
- Funkcja eventowa – gdy chcemy na onclick na przykład wywołać jakiś handler, ale przekazać mu argumenty
- Funkcja initializer – przekazywana do useState funkcja tworząca initial state (jeden raz, chyba że nie wiemy co robimy i przekażemy tam zawołanie funkcji)
- Funkcja reducer – przekazywana do useReducer, jeszcze poznamy
- Funkcja definiowana od zera przy każdym re-renderze – każda funkcja wewnątrz komponentu funkcyjnego, chyba że użyjemy useCallback (poznamy)
- Funkcja definiowana 1 raz, totalnie niezmienna – funkcja powyżej poziomu rfc
- Funkcja custom hook – musi zaczynać się od słowa use, każdy argument do niej przekazany jest traktowany jako zależność równa innym reaktywnym zmiennym będącym zazwyczaj zależnościami, może używać hooków
- Funkcja asynchroniczna – ani rfc nie może być asynchroniczne, ani callbacki do hooków, ale wewnątrz useEffect (albo wewnątrz rfc albo powyżej rfc) możemy utworzyć funkcję ze słówkiem async i wykorzystać ją z await wewnątrz useEffect
- Funkcja updater – przyjmuje poprzedni stan do callbacka i choć nie zna tego stanu, wie jak mu update robić
Tą ostatnią się teraz zajmiemy. Są dwa zastosowania do niej:
- Jeżeli chcemy wywołać setState kilka razy z rzędu w jednym handlerze
- Jeżeli chcemy wywalić state z tablicy zależności
Ok, najpierw ten pierwszy przypadek. Wyobraźmy sobie, że coś takiego mamy:
export default function Form() {
const [name, setName] = useState('Taylor');
const [age, setAge] = useState(42);
I teraz tak – chcemy zrobić inkrementację age 3 razy z rzędu:
function handleClick() {
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
}
I nie działa. Nie dość, że korzystamy z age, zmiennej stanowej (bardzo często jest to obarczone koniecznością dopisania do tablicy zależności np. w useEffect) to jeszcze głupi React nie ogarnia.
No, może nie głupi, dla niego stan się nie zmieni, póki handler się nie skończy, jest cały czas 42.
Teraz updater function:
function handleClick() {
setAge(a => a + 1); // setAge(42 => 43)
setAge(a => a + 1); // setAge(43 => 44)
setAge(a => a + 1); // setAge(44 => 45)
}
To a to tzw. prevState. Nie dość, że nie jest on zależnością (i handleClick nie wie, jaką ma wartość) to jeszcze podanie updatera powoduje, że bez problemu radzi sobie z wykonaniem inkrementacji 3 razy pod rząd.
Ok, teraz z Githuba o nazwie academind (też znamy twórca) podaję przykład korzystania z updatera do usuwania zbędnych zależności z tablicy:
import { useState, useEffect } from 'react';
export default function ProgressBar({ timer }) {
const [remainingTime, setRemainingTime] = useState(timer);
useEffect(() => {
const interval = setInterval(() => {
setRemainingTime((prevTime) => prevTime - 10);
}, 10);
return () => {
clearInterval(interval);
};
}, []);
return <progress value={remainingTime} max={timer} />;
}
Jak widać remainingTime nie jest zależnością useEffect. Funkcja setState nigdy nią nie jest, ale samo state już tak, natomiast korzystając z konwencji prevState i updatera możemy olać taką zależność i przekazać instrukcje jak ma być wykonany update niezależnie jaka tam jest wartość.
Bo warto wiedzieć – gdyby state tam było zależnością, to doszłoby do pętli nieskończonej:
- w setState korzystamy z state – 10
- w związku z tym dopisujemy state do zależności
- w związku z tym, ilekroć state się zmieni useEffect się odpala od nowa
Ok, więcej o React już niedługo…