React 18, to najnowsza aktualizacja, która miała swoją premierę 29.03.2022 roku. Czekaliśmy na nią prawie 1.5 roku po słynnej aktualizacji React 17, która ... nie wprowadziła tak naprawdę nic.
Na szczęście React 18 dodaje kilka interesujących zmian, jednak zanim do nich przejdziemy musimy sobie odpowiedzieć na jeszcze jedno istotne pytanie.
Jak zainstalować React 18?
Jeśli zaczynasz nowy projekt, wystarczy, że w konsoli wpiszesz:
npm i react@18 react-dom@18
lub
npm i react react-dom
ponieważ jest to aktualnie najnowsza wersja React.js, dlatego zainstaluje się automatycznie.
Jeśli aktualizujesz obecny projekt do React 18 zwróć na to uwagę!
Aby React 18 działał w Twoim obecnym projekcie musisz wprowadzić jedną istotną zmianę.
W miejscu, w którym renderujesz główny komponent aplikacji w DOM musisz podmienić aktualny kod:
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
na:
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />)
Jak czytamy w dokumentacji dzięki tej zmianie możemy korzystać z wszystkich ulepszeń i funkcjonalności, które pojawiły się w React 18.
Więcej informacji na ten temat znajdziesz tutaj.
Nowości w React 18
Automatic Batching
Batching, czyli grupowanie polega na tym, że React.js grupuje wiele aktualizacji state i wprowadza je w pojedynczym renderowaniu w celu uzyskania lepszej wydajności.
Takie rozwiązanie widzieliśmy już w poprzednich wersjach. Batching występował między innymi w ramach jednego event handlera. React.js renderował widok tylko raz na końcu funkcji.
Jednak nie działało to w innych miejscach, na przykład gdy podczas wywoływania fetch aktualizowaliśmy state kilkukrotnie widok renderował się przy każdej aktualizacji.
Świetnie widać to na poniższym przykładzie, który udostępnił React.js w swojej dokumentacji:
// Before React 18 only React events were batched
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// React will only re-render once at the end (that's batching!)
}
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React will render twice, once for each state update (no batching)
}, 1000);
Oraz w React 18:
// After React 18 updates inside of timeouts, promises,
// native event handlers or any other event are batched.
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// React will only re-render once at the end (that's batching!)
}
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React will only re-render once at the end (that's batching!)
}, 1000);
Jest to kluczowa zmiana, która ograniczy renderowanie aplikacji, co ma znacząco zwiększyć jej wydajność 🚀
Suspense
Suspense pozwala na określenie stanu ładowania w momencie, gdy nasz komponent nie jest jeszcze gotowy. Lekko skomplikowane określenie, ale już tłumaczę o co w nim chodzi.
Do tej pory, gdy pobieraliśmy zewnętrzne dane samodzielnie musieliśmy obsłużyć stan, w którym dane są w trakcie pobierania (na przykład loader):
const [isLoading, setLoading] = useState(true);
if data != null {
setLoading(true);
}
return(
<>
{ !loading &&
<Component />
}
{ loading &&
<Loading />
}
<>
);
Suspense przychodzi z pomocą i robi to za nas:
<Suspense fallback={<Loading/>}>
<Component data={data}/>
</Suspense>
Co najlepsze - nie jesteśmy już ograniczeni do używania Suspense tylko po stronie klienta. React 18 pozwala na użycie Suspense również używając Server Side Renderingu.
Transitions
Jest to nowa koncepcja React 18 za pomocą której jeteśmy w stanie określić, która aktualizacja stanu, a co za tym idzie renderowanie aplikacji ma wyższy priorytet.
Świetnym przykładem jest wybór filtra z listy rozwijanej. Użytkownik oczekuje, że sam przycisk filtra zareaguje natychmiast po kliknięciu. Jednak pokazanie filtrowanych wyników może pokazać się z niewielkim lub nawet niezauważalnym opóźnieniem.
Jeśli użytkownik ponownie zmieni filtr przed zakończeniem renderowania wyników, poprzednie transition się zatrzyma i poczeka na najnowsze wyniki filtracji.
import {startTransition} from 'react';
// Urgent: natychmiastowa aktualizacja
setInputValue(input);
startTransition(() => {
// Transition: aktualizacja z niższym priorytetem
setSearchQuery(input);
});
Aktualizacje znajdujące się w startTransition są traktowane jako mniej pilne i zostaną przerwane, jeśli pojawią się pilniejsze aktualizacje, takie jak kliknięcia lub naciśnięcia klawiszy.
Aby zarządzać transitions w React 18 wykorzystujemy:
- useTransition - hook, który zwraca aktualny stan naszego transition oraz funkcję do jej wywołania
- startTransition - metoda, którą poznaliśmy wyżej. Używamy jej wszędzie tam, gdzie nie można użyć hooków
Wykorzystanie useTransition:
function App() {
const [isPending, startTransition] = useTransition();
const [count, setCount] = useState(0);
function handleClick() {
startTransition(() => {
setCount(c => c + 1);
})
}
return (
<div>
{isPending && <Spinner />}
<button onClick={handleClick}>{count}</button>
</div>
);
}
useDeferredValue()
W tym miejscu warto jeszcze wspomnieć o nowym hooku useDeferredValue. Mówi on Reactowi, że powinien wyświetlić starszą wartość, zanim nowa będzie gotowa.
Zapobiega to bezczynnemu czekaniu na nowe dane, co również ma poprawić UX naszej aplikacji.
const deferredValue = useDeferredValue(value);
Możemy go wykorzystać na przykład, gdy nasz komponent pobiera i wyświetla dane. Dzięki funkcji useDeferredValue możemy pozwolić na pobieranie nowych danych w tle i stworzyć iluzję szybkiej i płynnej aktualizacji natychmiast wyświetlając starą wartość.
Podsumowanie
React 18 był jedną z najbardziej wyczekiwanych aktualizacji w ostatnim czasie. Wprowadza wiele świetnych rozwiązań, jednak zanim zaczniesz wszędzie wrzucać startTransition poczekałbym na feedback community.
W najbliższym czasie pewnie pojawią się najlepsze praktyki związane z tymi zmianami, więc warto śledzić temat na bieżąco.
Jeśli jesteś na początku nauki React.js sprawdź mój ostatni artykuł, w którym podaję 9 rzeczy, które musisz wiedzieć, zanim zaczniesz uczyć się React.js.