Nowości w React 19 - krótki przegląd

🤖 React Compiler
Większość nowości w React 19 zawdzięczamy kompilatorowi, więc co on właściwie robi?
React Compiler przekształca Twój kod Reacta w standardowy JavaScript. To on pilnuje teraz ponownych renderowań, co wyraźnie poprawia wydajność aplikacji. W praktyce oznacza to mniej zmartwień związanych z optymalizacją: React sam ustala, które komponenty i kiedy trzeba zoptymalizować oraz co powinno zostać ponownie wyrenderowane, co upraszcza pracę.

Idempotentność w React
Jednym z fundamentów, na których opiera się React Compiler, jest idempotentność komponentów. W tym kontekście oznacza ona, że komponent Reacta dla tych samych danych wejściowych powinien zwracać ten sam wynik, bez efektów ubocznych takich jak mutowanie propsów czy stanu. Ta zasada jest kluczowa dla optymalizacji wykonywanych przez kompilator, bo pozwala Reactowi automatycznie renderować ponownie dokładnie te fragmenty UI, których dotyczy zmiana stanu - dzięki temu aktualizacje są oszczędne, a wydajność rośnie.
Wpływ nowej wersji Reacta na idempotentność
Pojawienie się React Compilera w nadchodzącej wersji mocno zmienia znaczenie idempotentności w aplikacjach reactowych i to, jak jest egzekwowana. Trzymając się zasad JavaScriptu i Reacta, w tym idempotentności komponentów, programiści mogą zostawić optymalizację kompilatorowi. W efekcie ręczna memoizacja stosowana dziś, by zapobiegać nadmiarowym renderowaniom (useMemo, useCallback i memo), może stać się mniej potrzebna, a nawet zbędna, bo proces optymalizacji przejmuje kompilator.

Przykład użycia hooków wydajnościowych przed React 19:
import { useCallback, useMemo, useState } from "react";
function NameFilterApp() {
const [names, setNames] = useState([
"Tommy",
"Arthur",
"Polly",
"John",
"Grace",
]);
const [filter, setFilter] = useState("");
const handleFilterChange = useCallback((event) => {
setFilter(event.target.value);
}, []);
const filteredNames = useMemo(
() =>
names.filter((name) => name.toLowerCase().includes(filter.toLowerCase())),
[names, filter],
);
const totalLettersCount = useMemo(
() => filteredNames.reduce((acc, name) => acc + name.length, 0),
[filteredNames],
);
return (
<>
<input
type="text"
value={filter}
onChange={handleFilterChange}
placeholder="Filter names..."
/>
<div>
<strong>Filtered Names:</strong>
{filteredNames.length > 0 ? filteredNames.join(", ") : "No results"}
</div>
<div>
<strong>Total Number of Letters:</strong> {totalLettersCount}
</div>
</>
);
}
export default NameFilterApp;W tym przykładzie:
useStatesłuży do śledzenia bieżącego filtra (filter) i listy imion (names).useCallbackoptymalizuje funkcję obsługującą zmiany w polu tekstowym filtra, żeby uniknąć niepotrzebnego ponownego renderowania.useMemooptymalizuje obliczenia związane z filtrowaniem listy imion i liczeniem łącznej liczby liter w przefiltrowanych imionach. Dzięki temu obliczenia wykonują się tylko wtedy, gdy zmieni sięnameslubfilter, a nie przy każdym renderowaniu.
Nowy kompilator zoptymalizuje kod aplikacji automatycznie, więc możesz całkowicie usunąć z kodu wszystkie używane dotąd hooki wydajnościowe:
import { useState } from "react";
function NameFilterApp() {
const [names, setNames] = useState([
"Tommy",
"Arthur",
"Polly",
"John",
"Grace",
]);
const [filter, setFilter] = useState("");
const handleFilterChange = (event) => {
setFilter(event.target.value);
};
const filteredNames = names.filter((name) =>
name.toLowerCase().includes(filter.toLowerCase()),
);
const totalLettersCount = filteredNames.reduce(
(acc, name) => acc + name.length,
0,
);
return (
<>
<input
type="text"
value={filter}
onChange={handleFilterChange}
placeholder="Filter names..."
/>
<div>
<strong>Filtered Names:</strong>
{filteredNames.length > 0 ? filteredNames.join(", ") : "No results"}
</div>
<div>
<strong>Total Number of Letters:</strong> {totalLettersCount}
</div>
</>
);
}
export default NameFilterApp;
W React 19 nie musisz już też używać forwardRef.

Teraz możemy przekazać ref jako props i korzystać z niego tak jak z każdego innego propsa, co jest sporym ułatwieniem. Zobacz, co się zmieniło:

💪 Dyrektywy i akcje

'use client'
Dyrektywa 'use client' oznacza w React kod, który ma działać po stronie klienta. Umieszczając 'use client' na początku pliku, sygnalizujesz Reactowi i zgodnym bundlerom, że moduł wraz z zależnościami jest przeznaczony do wykonania po stronie klienta. Przydaje się to szczególnie w aplikacjach korzystających z komponentów serwerowych, bo pozwala wyraźnie oddzielić kod działający na serwerze od kodu działającego na kliencie.
przypadek użycia
Typowe zastosowanie 'use client' to interaktywne komponenty, które potrzebują dostępu do API przeglądarki albo muszą zachowywać stan między renderowaniami. Komponent z interaktywnymi formularzami, animacjami czy dostępem do local storage musi działać po stronie klienta.
'use client';
import { useState } from 'react';
function InteractiveComponent() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
Komponenty, które bezpośrednio manipulują DOM-em albo korzystają z API specyficznych dla przeglądarki, na przykład pobierają geolokalizację, odtwarzają dźwięk lub animują elementy przez Web Animations API, powinny być oznaczone do działania po stronie klienta.
'use client';
import { useEffect } from 'react';
function GeolocationComponent() {
useEffect(() => {
navigator.geolocation.getCurrentPosition(position => {
console.log(position);
});
}, []);
return <p>Check the console for the current position.</p>;
}
Ograniczenia i wady
- Podział kodu: Dyrektywa
'use client'pozwala wyraźnie odróżnić kod kliencki od serwerowego, ale programista musi tym podziałem zarządzać, co w większych aplikacjach zwiększa złożoność. - Większy bundle: Kod oznaczony do działania po stronie klienta trafia do przeglądarki, co może zwiększyć rozmiar bundle'a, zwłaszcza gdy po stronie klienta ma działać dużo kodu.
- Zarządzanie stanem: Komponenty klienckie same zarządzają swoim stanem, co może prowadzić do powielania logiki stanu między klientem a serwerem.
Mimo tych wyzwań dyrektywa 'use client' daje duże możliwości optymalizacji wydajności i poprawy wrażeń użytkownika, bo pozwala uruchamiać interaktywne komponenty po stronie klienta. Świadomie decydując, które części aplikacji powinny działać na kliencie, można znaleźć dobrą równowagę między wydajnością a bogactwem funkcji.
dokumentacja dyrektywy use client w React
'use server'
Dyrektywa 'use server' pozwala wywoływać funkcje serwerowe z poziomu klienta, oznaczając je jako akcje serwerowe. Umieszczenie 'use server' na początku ciała funkcji asynchronicznej sygnalizuje, że może ją wywołać kod kliencki - wtedy do serwera idzie żądanie sieciowe z serializowanymi argumentami.
Przykład akcji formularza
Typowe zastosowanie 'use server' to obsługa wysyłania formularzy. Formularz zapisu do newslettera może na przykład wywołać akcję serwerową, która doda adres e-mail do bazy danych.
// SignUpForm.js (Client Component)
import { useState } from 'react';
function SignUpForm({ submitEmail }) {
const [email, setEmail] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
await submitEmail({ email });
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<button type="submit">Sign Up</button>
</form>
);
}
// submitEmail.js (Server Action)
'use server';
export async function submitEmail({ email }) {
// Logic to add email to database
}
Mutacje danych
Akcje serwerowe można też wykorzystać do mutacji danych, takich jak aktualizacja profilu użytkownika czy dodanie komentarza, gdzie logika mutacji jest zamknięta w funkcji serwerowej.
Ograniczenia i wady
- Asynchroniczne wykonanie: Akcje serwerowe wymagają wywołania sieciowego, więc z natury są asynchroniczne, co może komplikować zarządzanie stanami UI i informacją zwrotną przy długotrwałych operacjach.
- Ograniczenia serializacji: Argumenty przekazywane do akcji serwerowych i zwracane przez nie wartości muszą być serializowalne. Ogranicza to typy danych, które można przesyłać między klientem a serwerem, i wyklucza funkcje, elementy JSX oraz inne typy nieserializowalne.
- Bezpieczeństwo: Akcje serwerowe można wywoływać z danymi kontrolowanymi przez klienta, dlatego trzeba koniecznie walidować i sanityzować dane wejściowe, by zapobiegać podatnościom takim jak SQL injection czy cross-site scripting (XSS).
- Zależność od opóźnień sieci: Szybkość reakcji akcji serwerowych zależy od warunków sieciowych. Może to wpływać na odczuwaną szybkość operacji uruchamianych przez użytkownika, szczególnie przy wolnym lub niestabilnym połączeniu.
Zalety
- Mniejsza złożoność po stronie klienta: Przenosząc operacje na serwer, upraszczasz logikę kliencką, zwłaszcza przy przetwarzaniu wrażliwych danych lub złożonych transakcjach.
- Dostęp do zasobów serwerowych: Akcje serwerowe mogą korzystać z zasobów po stronie serwera, takich jak bazy danych czy zewnętrzne API, do których klient nie ma bezpośredniego dostępu.
- Większe bezpieczeństwo: Wykonywanie akcji na serwerze daje lepszą kontrolę nad bezpieczeństwem, bo wrażliwe operacje i dostęp do danych można ściślej kontrolować i monitorować.
Dyrektywa 'use server' daje wygodny model włączania logiki serwerowej do aplikacji reactowych i pozwala budować wydajniejsze oraz bezpieczniejsze aplikacje webowe. Rozsądnie stosując akcje serwerowe, można poprawić wrażenia użytkownika, zachowując niezawodność i skalowalność aplikacji.
dokumentacja dyrektywy use server w React
Obie dyrektywy ułatwiają tworzenie aplikacji z wyraźnym rozdzieleniem odpowiedzialności między logiką kliencką i serwerową, co przekłada się na efektywność i wydajność aplikacji w React.
🪝 Nowe hooki Reacta
Hook use()

Hook use() w React 19 wprowadza elastyczny sposób dostępu do zasobów, takich jak promise'y czy konteksty, wewnątrz komponentów. W przeciwieństwie do innych hooków można go używać w pętlach i instrukcjach warunkowych. Hook został zaprojektowany z myślą o współpracy z Suspense i error boundaries, co daje wygodniejszy sposób asynchronicznego pobierania danych i zarządzania kontekstem
Pobieranie danych przed React 19
import { useEffect, useState } from "react";
const FetchDataComponent = () => {
const [posts, setPosts] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts",
);
if (!response.ok) {
throw new Error("Data could not be fetched!");
}
const data = await response.json();
setPosts(data);
} catch (error) {
setError(error.message);
} finally {
setIsLoading(false);
}
};
fetchData();
}, []);
if (isLoading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error}</div>;
}
return (
<div>
<h1>Test Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
};
export default FetchDataComponent;
W tym komponencie wykonujemy następujące kroki:
- Definicja stanu: Hookiem
useStatetworzymy stany przechowujące wpisy, stan ładowania (isLoading) i ewentualne błędy (error). - Pobieranie danych: Hookiem
useEffectpo załadowaniu komponentu wykonujemy asynchroniczne żądaniefetchdo API. FunkcjafetchDatajest asynchroniczna, więc możemy użyćawait, żeby poczekać na odpowiedź sieciową. - Zarządzanie stanem: W zależności od odpowiedzi aktualizujemy stan wpisów, ładowania i błędu. Do obsługi błędów podczas żądania używamy
try...catch. - Renderowanie: Na podstawie stanu komponent renderuje informację o ładowaniu, błąd albo listę wpisów.
Pobieranie danych w React 19 z hookiem use()
// 1. Import React Hooks and Components
import { use, Suspense } from "react";
// 2. Define the Data Fetching Function
// This function fetches posts from a remote API and returns a promise.
const fetchPosts = () => {
return fetch("https://jsonplaceholder.typicode.com/posts")
.then((response) => {
if (!response.ok) {
throw new Error("Data could not be fetched!");
}
return response.json();
});
};
// Component that uses the `use` hook to fetch and display posts
const FetchDataComponent = () => {
// 3. Use the `use` Hook to Fetch Data
// The `use` hook is called with the promise returned by `fetchPosts`.
const postsPromise = fetchPosts();
const posts = use(postsPromise);
// 5. Map Over the Fetched Data
// This renders a list of posts by mapping over the array of fetched posts.
return (
<div>
<h1>Test Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
};
// 6. Render the Component Tree
// The `App` component that renders the `FetchDataComponent` within a `Suspense` component.
export default function App() {
return (
// 4. Handle Loading State with `Suspense`
// `Suspense` shows a fallback UI while the data is being fetched.
<Suspense fallback={<div>Loading...</div>}>
<FetchDataComponent />
</Suspense>
);
}
W tym podejściu, z użyciem nowego hooka use(), wykonujemy następujące kroki:
- Import hooków i komponentów Reacta: Kod zaczyna się od zaimportowania potrzebnych hooków i komponentów z Reacta, w tym hooka
usei komponentuSuspense. - Definicja funkcji pobierającej dane: Poza komponentem Reacta definiujemy funkcję
fetchPosts. Odpowiada ona za pobranie danych ze zdalnego API (https://jsonplaceholder.typicode.com/posts). Korzysta z APIfetch, wysyła żądanie GET i zwraca promise, który rozwiązuje się danymi albo odrzuca błędem. - Użycie hooka
usedo pobrania danych: W komponencieFetchDataComponentwywołujemy hookuse, przekazując mu promise zwrócony przezfetchPosts. Hookuseodczytuje wartość tego promise'a. Warto pamiętać, że sposób użycia hookausez promise'em może się różnić w zależności od konfiguracji projektu. - Obsługa stanu ładowania przez
Suspense:FetchDataComponentjest owinięty komponentemSuspensew nadrzędnym komponencieApp. KomponentSuspensesłuży do wyświetlania fallback UI (w tym przypadku<div>Loading...</div>), dopóki promise oczekuje na rozwiązanie. Gdy to nastąpi, fallback UI zostaje zastąpiony interfejsemFetchDataComponentz pobranymi danymi. - Mapowanie pobranych danych: W
FetchDataComponentmapujemy pobrane dane (zakładamy, że to tablica wpisów), żeby wyrenderować ich listę. Każdy wpis jest wyświetlany jako element listy (<li>) ze swoim tytułem. - Renderowanie drzewa komponentów: Na koniec renderowany jest komponent
App(zawierającySuspenseiFetchDataComponent). Po wyrenderowaniuAppnajpierw pokazuje stan ładowania, a gdy dane zostaną pobrane i promise się rozwiąże, wyświetla listę wpisów.
Odczyt kontekstu przed React 19
W tym przykładzie ThemeContext.Provider pozwala przekazać wartość "dark" wszystkim komponentom w drzewie, które korzystają z tego kontekstu. ThemeComponent odczytuje wartość kontekstu za pomocą useContext.
import React from 'react';
import { ThemeContext, ThemeComponent } from './Context';
// The main application component with a context provider
const App: React.FC = () => {
return (
<ThemeContext.Provider value="dark">
<ThemeComponent />
</ThemeContext.Provider>
);
}
export default App;import React, { createContext, useContext, ReactNode } from 'react';
// Creating a Context with a default value of 'light'
export const ThemeContext = createContext<string>('light');
interface ThemeComponentProps {
children?: ReactNode;
}
// Component using useContext
export const ThemeComponent: React.FC<ThemeComponentProps> = () => {
const theme = useContext(ThemeContext);
return <div>Current theme: {theme}</div>;
}
W tym przykładzie ThemeContext.Provider pozwala przekazać wartość "dark" wszystkim komponentom w drzewie, które korzystają z tego kontekstu. ThemeComponent odczytuje wartość kontekstu za pomocą useContext.
Odczyt kontekstu w React 19
Teraz zamiast useContext() będziemy mieć use(context).
import React, { createContext, ReactNode, use } from 'react';
// Creating a Context with a default value of 'light'
export const ThemeContext = createContext<string>('light');
interface ThemeComponentProps {
children?: ReactNode;
}
// Component using the new use() hook
export const ThemeComponent: React.FC<ThemeComponentProps> = () => {
const theme = use(ThemeContext);
return <div>Current theme: {theme}</div>;
}
Zalety hooka use
- Asynchroniczne ładowanie zasobów: Różne zasoby można bez trudu ładować asynchronicznie, co poprawia wydajność i wrażenia użytkownika.
- Prostsze pobieranie danych: Zastępuje hook
useEffectprzy pobieraniu danych, dając prostszy sposób wysyłania żądań do API i obsługi odpowiedzi. - Dostęp do danych z kontekstu: Może zastąpić
useContextprzy odczycie kontekstu, co ogranicza boilerplate i upraszcza zarządzanie kontekstem.
Praktyczne zastosowania
- Pobieranie danych: Hook
usesprawia, że pobieranie danych z API jest bardziej intuicyjne. W połączeniu z React Suspense pozwala wyświetlić fallback UI podczas ładowania, a po zakończeniu pobierania płynnie zaktualizować interfejs. - Zarządzanie kontekstem: Upraszcza odczyt kontekstu, zastępując
useContexthookiemuse. Łatwiej jest korzystać z kontekstu w komponentach, co dodatkowo zmniejsza złożoność aplikacji w React.
oficjalna dokumentacja hooka use()
Hook useFormStatus()

Hook useFormStatus w React dostarcza przydatnych informacji o statusie wysyłania formularza, co jest kluczowe przy obsłudze stanów formularza i interakcji użytkownika. Został zaprojektowany z myślą o komponentach renderowanych wewnątrz formularza i zwraca obiekt statusu ze szczegółami dotyczącymi wysyłania.
const { pending, data, method, action } = useFormStatus();Jak to działa
Aby skutecznie korzystać z hooka useFormStatus, Twój komponent, na przykład przycisk Submit, musi znajdować się wewnątrz elementu <form>. Hook podłącza się pod proces wysyłania formularza i udostępnia właściwości takie jak pending, informującą, czy formularz jest właśnie wysyłany.
Użycie
- Parametry:
useFormStatusnie przyjmuje żadnych parametrów. - Zwraca: Hook zwraca obiekt statusu z kilkoma właściwościami:
pending: Wartość logiczna informująca, czy formularz jest w trakcie wysyłania. Wartośćtrueoznacza, że wysyłanie trwa.data: Obiekt zgodny z interfejsem FormData, zawierający dane wysyłane przez formularz. Będzie mieć wartośćnull, jeśli żadne wysyłanie nie trwa albo komponent nie znajduje się w formularzu.method: Ciąg znaków 'get' lub 'post' określający metodę HTTP używaną przez formularz. Metoda wynika z atrybutumethodformularza, a jeśli go nie podano, domyślnie jest to 'get'.action: Funkcja przekazana do propsaactionformularza. Będzie mieć wartośćnull, jeśli komponent nie jest objęty nadrzędnym<form>, jeśli propsactionjest adresem URI albo jeśli nie podano propsaaction.
Ważne uwagi
- Zależność od formularza:
useFormStatustrzeba wywołać w komponencie znajdującym się wewnątrz<form>. Hook dostarcza informacje wyłącznie o nadrzędnym<form>i poza tym kontekstem nie zadziała. - Zakres informacji o statusie: Hook śledzi wyłącznie status wysyłania nadrzędnego
<form>. Nie obejmuje formularzy zagnieżdżonych w tym samym komponencie ani w komponentach potomnych.
Przykład:
import { useFormStatus } from "react-dom";
import { submitForm } from "./actions.js";
function Submit() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? "Submitting..." : "Submit"}
</button>
);
}
function Form({ action }) {
return (
<form action={action}>
<Submit />
</form>
);
}
export default function App() {
return <Form action={submitForm} />;
}Korzystając z useFormStatus w formularzach Reacta, możesz dać użytkownikowi lepszą informację zwrotną podczas wysyłania i uzyskać większą kontrolę nad tym procesem, co czyni interfejs bardziej responsywnym i czytelnym.
Hook useFormState()
Hook useFormState oferuje nowatorskie podejście do zarządzania stanem formularza na podstawie wyników akcji formularza. Jest szczególnie przydatny w połączeniu z komponentami serwerowymi, bo poprawia interaktywność formularzy, jeszcze zanim JavaScript po stronie klienta w pełni się załaduje.
Co daje useFormState
useFormState pozwala tworzyć stan komponentu aktualizowany w odpowiedzi na akcje formularza. Przekazując do useFormState funkcję akcji i stan początkowy, dostajesz nową akcję formularza oraz najnowszy stan, z których możesz korzystać w komponentach formularza.
Jak używać useFormState
Zdefiniujmy prostą funkcję akcji symulującą wysłanie formularza, które może zakończyć się sukcesem albo błędem w zależności od danych wejściowych. Następnie zaktualizujemy komponent StatefulForm, żeby wyświetlał komunikaty o sukcesie i błędzie.
Akcja formularza symulująca sukces i błąd
// actions.js
// Simulate a form submission that can either succeed or fail.
export async function submitForm(previousState, formData) {
try {
// Simulate checking formData for success or failure criteria.
if (formData.get('exampleField') === 'success') {
return { status: 'success', message: 'Form submitted successfully.' };
} else {
throw new Error('Form submission failed.');
}
} catch (error) {
return { status: 'error', message: error.message };
}
}
Komponent StatefulForm z komunikatami o sukcesie i błędzie
import React from 'react';
import { useFormState } from 'react-dom';
import { submitForm } from './actions';
function StatefulForm() {
const [state, formAction] = useFormState(submitForm, { status: 'idle', message: '' });
const handleSubmit = (event) => {
event.preventDefault();
const formData = new FormData(event.target);
formAction(formData);
};
return (
<form onSubmit={handleSubmit}>
<input name="exampleField" placeholder="Type 'success' for success" />
<button type="submit">Submit</button>
{/* Feedback based on the form's submission state */}
{state.status === 'success' && <p style={{ color: 'green' }}>{state.message}</p>}
{state.status === 'error' && <p style={{ color: 'red' }}>{state.message}</p>}
</form>
);
}
export default StatefulForm;
Wyjaśnienie kodu
- Funkcja akcji (
submitForm): Ta funkcja symuluje proces wysyłania formularza. Zwraca obiekt stanu zawierającystatusimessage.statusmoże przyjąć wartośćsuccessluberrorw zależności od wysłanych danych. - Komponent formularza (
StatefulForm):- Inicjalizuje stan formularza za pomocą
useFormState, ze stanem początkowym oznaczającym bezczynność. - Renderuje prosty formularz z polem tekstowym i przyciskiem wysyłania. Wysłanie obsługuje
handleSubmit, który tworzy obiektFormDatai przekazuje go doformAction. - Wyświetla komunikaty zależne od stanu wysyłania. Komunikaty o sukcesie są zielone, a o błędzie czerwone.
- Inicjalizuje stan formularza za pomocą
Ten przykład pokazuje, jak za pomocą useFormState dynamicznie zarządzać informacją zwrotną po wysłaniu formularza, dając użytkownikowi natychmiastową i konkretną reakcję na jego działania.
Hook useOptimistic

Hook useOptimistic w React pozwala sprawić, że interfejs sprawia wrażenie szybszego, bo optymistycznie aktualizuje go przed zakończeniem długotrwałych operacji, takich jak żądania sieciowe. Takie podejście szczególnie przydaje się tam, gdzie zależy nam na natychmiastowej reakcji na działania użytkownika, na przykład przy wysyłaniu formularzy czy aktualizacji danych.
Jak działa useOptimistic
useOptimistic pozwala tymczasowo zaktualizować UI na podstawie oczekiwanego wyniku, przy założeniu, że operacja się powiedzie. Przyjmuje fragment stanu i funkcję aktualizującą (updateFn), która określa, jak połączyć bieżący stan z optymistyczną aktualizacją. Hook zwraca dwa elementy:
optimisticState: Stan uwzględniający optymistyczną aktualizację.addOptimistic: Funkcja, którą wywołujesz z wartością optymistycznej aktualizacji.
Przykład z useOptimistic
Stwórzmy prosty przykład optymistycznej aktualizacji UI przy wysyłaniu wiadomości. Interfejs od razu pokaże wiadomość ze statusem "Sending...", dopóki operacja się nie zakończy.
import React, { useState } from 'react';
import { useOptimistic } from 'react';
function MessageSender() {
const [messages, setMessages] = useState([]);
const [messageInput, setMessageInput] = useState('');
const [optimisticMessages, addOptimistic] = useOptimistic(messages, (currentMessages, optimisticMessage) => {
// Merge the current messages with the optimistic update
return [...currentMessages, optimisticMessage];
});
const sendMessage = async (message) => {
addOptimistic({ id: Date.now(), text: message, status: 'Sending...' });
try {
// Simulate a network request to send the message
await new Promise(resolve => setTimeout(resolve, 1000));
// After the request completes, update the actual state
setMessages(prev => [...prev, { id: Date.now(), text: message, status: 'Sent' }]);
} catch (error) {
console.error('Failed to send message:', error);
}
};
const handleSubmit = (event) => {
event.preventDefault();
sendMessage(messageInput);
setMessageInput('');
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
value={messageInput}
onChange={(e) => setMessageInput(e.target.value)}
placeholder="Type a message"
/>
<button type="submit">Send</button>
</form>
<ul>
{optimisticMessages.map(msg => (
<li key={msg.id}>{msg.text} - {msg.status}</li>
))}
</ul>
</div>
);
}
Wyjaśnienie kodu
- Inicjalizacja stanu: Komponent przechowuje dwie zmienne stanu:
messagesz faktycznymi wiadomościami imessageInputz bieżącą wartością pola. - Stan optymistyczny:
useOptimistictworzy stanoptimisticMessages, który łączy faktyczne wiadomości z optymistycznymi aktualizacjami. - Wysyłanie wiadomości: Funkcja
sendMessageoptymistycznie aktualizuje UI za pomocąaddOptimistic, od razu pokazując wiadomość ze statusem "Sending...". Następnie symuluje żądanie sieciowe, a po jego zakończeniu aktualizuje stanmessages, odnotowując, że wiadomość została wysłana. - Renderowanie UI: Komponent renderuje formularz do wpisywania wiadomości i ich listę. Lista odzwierciedla stan
optimisticMessages, pokazując optymistycznie status "Sending..." do czasu zakończenia symulowanego żądania.
Ten przykład pokazuje, jak useOptimistic poprawia wrażenia użytkownika, dając natychmiastową reakcję na działania, których wynik jest spodziewany, ale jeszcze niepotwierdzony - dzięki temu aplikacja sprawia wrażenie szybszej i bardziej responsywnej.
Podsumowanie trzech poznanych hooków do formularzy

Podsumowanie
React 19 wprowadza zestaw funkcji i usprawnień, które mają poprawić wygodę pracy programistów i wydajność aplikacji. W centrum tych zmian jest React Compiler, który optymalizuje ponowne renderowania i ogólną wydajność aplikacji, znacznie ograniczając potrzebę ręcznych optymalizacji. Nowa wersja ma usprawnić proces tworzenia aplikacji i pozwolić pisać bardziej czytelny i zwięzły kod.
Najważniejsze cechy React 19:
- React Compiler: Główny element React 19, który przekształca kod Reacta w zoptymalizowany JavaScript, automatyzując optymalizacje wydajności i ograniczając niepotrzebne renderowania. Dzięki temu mniej polegamy na ręcznych technikach memoizacji, takich jak
useCallback(),useMemo()imemo(). - Prostsze tworzenie aplikacji: Skoro optymalizacją zajmuje się kompilator, kod komponentów może być prostszy. Pokazany przykład przechodzi od wersji wymagającej hooków wydajnościowych do czystszej wersji bez nich, właśnie dzięki optymalizacjom kompilatora.
- Dyrektywy i akcje: React 19 wprowadza dyrektywy
'use client'i'use server', które wyraźniej rozdzielają logikę kliencką i serwerową, dzięki czemu kod jest wydajniejszy i lepiej zorganizowany. Dyrektywa'use server'ułatwia wdrażanie akcji serwerowych, upraszczając mutacje danych i wysyłanie formularzy. - Nowe hooki Reacta: Zestaw nowych hooków, w tym
use()do dostępu do zasobów,useFormStatus()do statusu wysyłania formularza iuseOptimisticdo optymistycznych aktualizacji UI, dodatkowo ułatwia pracę, upraszczając asynchroniczne pobieranie danych, zarządzanie stanem formularzy i budowanie responsywnych interfejsów.
React 19 nie skupia się na nowej składni czy koncepcjach, które utrudniałyby naukę, lecz na dopracowaniu frameworka: mniej boilerplate'u, lepsza wydajność i bardziej intuicyjna praca z Reactem. Ta wersja to duży krok w stronę wydajniejszych i przyjaźniejszych programistom aplikacji w React.
Dziękuję za poświęcony czas i przeczytanie tego wpisu. Mam nadzieję, że te przemyślenia okazały się dla Ciebie wartościowe i dały Ci do myślenia. Nie ma tu na razie sekcji komentarzy, ale bardzo doceniam Twoje zaangażowanie i zainteresowanie.
Rozwijaj się, ucz i inspiruj innych.
Na razie!