Prozkoumejte hook useActionState v Reactu pro zjednodušenou správu stavu spouštěnou asynchronními akcemi. Zvyšte efektivitu a uživatelský prožitek vaší aplikace.
Implementace React useActionState: Správa stavu na základě akcí
Hook useActionState od Reactu, představený v nedávných verzích, nabízí propracovaný přístup ke správě aktualizací stavu vyplývajících z asynchronních akcí. Tento výkonný nástroj zjednodušuje proces zpracování mutací, aktualizace UI a správy chybových stavů, zejména při práci s React Server Components (RSC) a serverovými akcemi. Tato příručka prozkoumá složitosti useActionState a poskytne praktické příklady a osvědčené postupy pro implementaci.
Pochopení potřeby správy stavu na základě akcí
Tradiční správa stavu v Reactu často zahrnuje oddělenou správu stavů načítání a chyb v rámci komponent. Když akce (např. odeslání formuláře, načítání dat) spustí aktualizaci stavu, vývojáři obvykle spravují tyto stavy pomocí více volání useState a potenciálně složité podmíněné logiky. useActionState poskytuje čistší a integrovanější řešení.
Zvažte jednoduchý scénář odeslání formuláře. Bez useActionState byste mohli mít:
- Stavovou proměnnou pro data formuláře.
- Stavovou proměnnou pro sledování, zda se formulář odesílá (stav načítání).
- Stavovou proměnnou pro uložení chybových zpráv.
Tento přístup může vést k rozvláčnému kódu a potenciálním nekonzistencím. useActionState konsoliduje tyto aspekty do jediného hooku, čímž zjednodušuje logiku a zlepšuje čitelnost kódu.
Představení useActionState
Hook useActionState přijímá dva argumenty:
- Asynchronní funkci („akci“), která provádí aktualizaci stavu. Může to být serverová akce nebo jakákoli asynchronní funkce.
- Počáteční hodnotu stavu.
Vrací pole obsahující dva prvky:
- Aktuální hodnotu stavu.
- Funkci pro spuštění akce. Tato funkce automaticky spravuje stavy načítání a chyb spojené s akcí.
Zde je základní příklad:
import { useActionState } from 'react';
async function updateServer(prevState, formData) {
// Simulace asynchronní aktualizace serveru.
await new Promise(resolve => setTimeout(resolve, 1000));
const data = Object.fromEntries(formData);
if (data.name === "error") {
return 'Aktualizace serveru se nezdařila.';
}
return `Jméno aktualizováno na: ${data.name}`;
}
function MyComponent() {
const [state, dispatch] = useActionState(updateServer, 'Počáteční stav');
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const result = await dispatch(formData);
console.log(result);
}
return (
);
}
V tomto příkladu:
updateServerje asynchronní akce, která simuluje aktualizaci serveru. Přijímá předchozí stav a data z formuláře.useActionStateinicializuje stav s hodnotou 'Počáteční stav' a vrací aktuální stav a funkcidispatch.- Funkce
handleSubmitvoládispatchs daty z formuláře.useActionStateautomaticky zpracovává stavy načítání a chyb během provádění akce.
Zpracování stavů načítání a chyb
Jednou z klíčových výhod useActionState je jeho vestavěná správa stavů načítání a chyb. Funkce dispatch vrací promise, který se resolvuje s výsledkem akce. Pokud akce vyhodí chybu, promise se rejectuje s touto chybou. Můžete to využít k odpovídající aktualizaci UI.
Upravme předchozí příklad tak, aby zobrazoval zprávu o načítání a chybovou zprávu:
import { useActionState } from 'react';
import { useState } from 'react';
async function updateServer(prevState, formData) {
// Simulace asynchronní aktualizace serveru.
await new Promise(resolve => setTimeout(resolve, 1000));
const data = Object.fromEntries(formData);
if (data.name === "error") {
throw new Error('Aktualizace serveru se nezdařila.');
}
return `Jméno aktualizováno na: ${data.name}`;
}
function MyComponent() {
const [state, dispatch] = useActionState(updateServer, 'Počáteční stav');
const [isSubmitting, setIsSubmitting] = useState(false);
const [errorMessage, setErrorMessage] = useState(null);
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
setIsSubmitting(true);
setErrorMessage(null);
try {
const result = await dispatch(formData);
console.log(result);
} catch (error) {
console.error("Chyba při odesílání:", error);
setErrorMessage(error.message);
} finally {
setIsSubmitting(false);
}
}
return (
);
}
Klíčové změny:
- Přidali jsme stavové proměnné
isSubmittingaerrorMessagepro sledování stavů načítání a chyb. - V
handleSubmitnastavímeisSubmittingnatruepřed volánímdispatcha zachytáváme případné chyby pro aktualizacierrorMessage. - Deaktivujeme odesílací tlačítko během odesílání a podmíněně zobrazujeme zprávy o načítání a chybách.
Použití useActionState se Server Actions v React Server Components (RSC)
useActionState exceluje při použití s React Server Components (RSC) a serverovými akcemi. Serverové akce jsou funkce, které běží na serveru a mohou přímo měnit datové zdroje. Umožňují provádět operace na straně serveru bez nutnosti psát API endpointy.
Poznámka: Tento příklad vyžaduje prostředí Reactu nakonfigurované pro Server Components a Server Actions.
// app/actions.js (Serverová akce)
'use server';
import { cookies } from 'next/headers'; // Příklad pro Next.js
export async function updateName(prevState, formData) {
const name = formData.get('name');
if (!name) {
return 'Prosím, zadejte jméno.';
}
try {
// Simulace aktualizace databáze.
await new Promise(resolve => setTimeout(resolve, 1000));
cookies().set('userName', name);
return `Jméno aktualizováno na: ${name}`; // Úspěch!
} catch (error) {
console.error("Aktualizace databáze selhala:", error);
return 'Aktualizace jména se nezdařila.'; // Důležité: Vraťte zprávu, nevyhazujte chybu
}
}
// app/page.jsx (React Server Component)
'use client';
import { useActionState } from 'react';
import { updateName } from './actions';
function MyComponent() {
const [state, dispatch] = useActionState(updateName, 'Počáteční stav');
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const result = await dispatch(formData);
console.log(result);
}
return (
);
}
export default MyComponent;
V tomto příkladu:
updateNameje serverová akce definovaná vapp/actions.js. Přijímá předchozí stav a data z formuláře, aktualizuje databázi (simulovaně) a vrací zprávu o úspěchu nebo chybě. Klíčové je, že akce vrací zprávu, místo aby vyhazovala chybu. Server Actions preferují vracení informativních zpráv.- Komponenta je označena jako klientská (
'use client'), aby mohla používat hookuseActionState. - Funkce
handleSubmitvoládispatchs daty z formuláře.useActionStateautomaticky spravuje aktualizaci stavu na základě výsledku serverové akce.
Důležité aspekty pro Server Actions
- Zpracování chyb v Server Actions: Místo vyhazování chyb vraťte smysluplnou chybovou zprávu z vaší Server Action.
useActionStatebude tuto zprávu považovat za nový stav. To umožňuje elegantní zpracování chyb na klientovi. - Optimistické aktualizace: Serverové akce lze použít s optimistickými aktualizacemi pro zlepšení vnímaného výkonu. Můžete okamžitě aktualizovat UI a vrátit změny zpět, pokud akce selže.
- Revalidace: Po úspěšné mutaci zvažte revalidaci cachovaných dat, aby UI odráželo nejnovější stav.
Pokročilé techniky s useActionState
1. Použití reduceru pro komplexní aktualizace stavu
Pro složitější logiku stavu můžete kombinovat useActionState s reducer funkcí. To vám umožní spravovat aktualizace stavu předvídatelným a udržitelným způsobem.
import { useActionState } from 'react';
import { useReducer } from 'react';
const initialState = {
count: 0,
message: 'Počáteční stav',
};
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
case 'SET_MESSAGE':
return { ...state, message: action.payload };
default:
return state;
}
}
async function updateState(state, action) {
// Simulace asynchronní operace.
await new Promise(resolve => setTimeout(resolve, 500));
switch (action.type) {
case 'INCREMENT':
return reducer(state, action);
case 'DECREMENT':
return reducer(state, action);
case 'SET_MESSAGE':
return reducer(state, action);
default:
return state;
}
}
function MyComponent() {
const [state, dispatch] = useActionState(updateState, initialState);
return (
Počet: {state.count}
Zpráva: {state.message}
);
}
2. Optimistické aktualizace s useActionState
Optimistické aktualizace zlepšují uživatelský prožitek tím, že okamžitě aktualizují UI, jako by akce byla úspěšná, a poté aktualizaci vrátí zpět, pokud akce selže. Díky tomu může vaše aplikace působit svižněji.
import { useActionState } from 'react';
import { useState } from 'react';
async function updateServer(prevState, formData) {
// Simulace asynchronní aktualizace serveru.
await new Promise(resolve => setTimeout(resolve, 1000));
const data = Object.fromEntries(formData);
if (data.name === "error") {
throw new Error('Aktualizace serveru se nezdařila.');
}
return `Jméno aktualizováno na: ${data.name}`;
}
function MyComponent() {
const [name, setName] = useState('Počáteční jméno');
const [state, dispatch] = useActionState(async (prevName, newName) => {
try {
const result = await updateServer(prevName, {
name: newName,
});
return newName; // Aktualizovat při úspěchu
} catch (error) {
// Vrátit zpět při chybě
console.error("Aktualizace selhala:", error);
setName(prevName);
return prevName;
}
}, name);
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const newName = formData.get('name');
setName(newName); // Optimisticky aktualizovat UI
await dispatch(newName);
}
return (
);
}
3. Debouncing akcí
V některých scénářích můžete chtít akce „debouncovat“, abyste zabránili jejich příliš častému spouštění. To může být užitečné například u vyhledávacích polí, kde chcete spustit akci až poté, co uživatel na určitou dobu přestane psát.
import { useActionState } from 'react';
import { useState, useEffect } from 'react';
async function searchItems(prevState, query) {
// Simulace asynchronního vyhledávání.
await new Promise(resolve => setTimeout(resolve, 500));
return `Výsledky vyhledávání pro: ${query}`;
}
function MyComponent() {
const [query, setQuery] = useState('');
const [state, dispatch] = useActionState(searchItems, 'Počáteční stav');
useEffect(() => {
const timeoutId = setTimeout(() => {
if (query) {
dispatch(query);
}
}, 300); // Debounce po dobu 300ms
return () => clearTimeout(timeoutId);
}, [query, dispatch]);
return (
setQuery(e.target.value)}
/>
Stav: {state}
);
}
Doporučené postupy pro useActionState
- Udržujte akce čisté: Zajistěte, aby vaše akce byly čisté funkce (nebo co nejblíže tomu). Neměly by mít vedlejší účinky kromě aktualizace stavu.
- Zpracovávejte chyby elegantně: Vždy zpracovávejte chyby ve svých akcích a poskytujte uživateli informativní chybové zprávy. Jak bylo uvedeno výše u Server Actions, upřednostňujte vrácení chybové zprávy jako řetězce ze serverové akce, místo vyhazování chyby.
- Optimalizujte výkon: Mějte na paměti dopady vašich akcí na výkon, zejména při práci s velkými datovými sadami. Zvažte použití technik memoizace, abyste se vyhnuli zbytečným přerenderováním.
- Zvažte přístupnost: Zajistěte, aby vaše aplikace zůstala přístupná všem uživatelům, včetně těch s postižením. Poskytněte vhodné ARIA atributy a klávesnicovou navigaci.
- Důkladné testování: Pište jednotkové a integrační testy, abyste zajistili, že vaše akce a aktualizace stavu fungují správně.
- Internacionalizace (i18n): Pro globální aplikace implementujte i18n pro podporu více jazyků a kultur.
- Lokalizace (l10n): Přizpůsobte svou aplikaci konkrétním lokalitám poskytováním lokalizovaného obsahu, formátů data a symbolů měn.
useActionState vs. ostatní řešení pro správu stavu
Ačkoliv useActionState poskytuje pohodlný způsob, jak spravovat aktualizace stavu založené na akcích, nenahrazuje všechna řešení pro správu stavu. Pro komplexní aplikace s globálním stavem, který je třeba sdílet mezi více komponentami, mohou být vhodnější knihovny jako Redux, Zustand nebo Jotai.
Kdy použít useActionState:
- Jednoduché až středně složité aktualizace stavu.
- Aktualizace stavu úzce spojené s asynchronními akcemi.
- Integrace s React Server Components a Server Actions.
Kdy zvážit jiná řešení:
- Komplexní správa globálního stavu.
- Stav, který je třeba sdílet mezi velkým počtem komponent.
- Pokročilé funkce jako time-travel debugging nebo middleware.
Závěr
Hook useActionState od Reactu nabízí výkonný a elegantní způsob, jak spravovat aktualizace stavu spouštěné asynchronními akcemi. Konsolidací stavů načítání a chyb zjednodušuje kód a zlepšuje čitelnost, zejména při práci s React Server Components a serverovými akcemi. Pochopení jeho silných stránek a omezení vám umožní zvolit správný přístup ke správě stavu pro vaši aplikaci, což vede k udržitelnějšímu a efektivnějšímu kódu.
Dodržováním doporučených postupů uvedených v této příručce můžete efektivně využít useActionState ke zlepšení uživatelského prožitku a vývojového workflow vaší aplikace. Nezapomeňte zvážit složitost vaší aplikace a zvolit řešení pro správu stavu, které nejlépe vyhovuje vašim potřebám. Od jednoduchého odesílání formulářů po složité datové mutace může být useActionState cenným nástrojem ve vašem arzenálu pro vývoj v Reactu.