Hrvatski

Otključajte moć Reactovog useActionState hooka. Naučite kako pojednostavljuje upravljanje formama, obrađuje stanja čekanja i poboljšava korisničko iskustvo uz praktične, detaljne primjere.

React useActionState: Sveobuhvatan vodič za moderno upravljanje formama

Svijet web razvoja neprestano se razvija, a React ekosustav je na čelu tih promjena. S nedavnim verzijama, React je uveo moćne značajke koje iz temelja poboljšavaju način na koji gradimo interaktivne i otporne aplikacije. Među najutjecajnijima od njih je useActionState hook, ključna promjena za rukovanje formama i asinkronim operacijama. Ovaj hook, prethodno poznat kao useFormState u eksperimentalnim izdanjima, sada je stabilan i neophodan alat za svakog modernog React developera.

Ovaj sveobuhvatni vodič provest će vas kroz detaljnu analizu useActionState hooka. Istražit ćemo probleme koje rješava, njegovu osnovnu mehaniku i kako ga iskoristiti uz komplementarne hookove poput useFormStatus za stvaranje vrhunskog korisničkog iskustva. Bilo da gradite jednostavnu kontaktnu formu ili složenu aplikaciju s velikom količinom podataka, razumijevanje useActionState hooka učinit će vaš kod čišćim, deklarativnijim i robusnijim.

Problem: Složenost tradicionalnog upravljanja stanjem forme

Prije nego što možemo cijeniti eleganciju useActionState hooka, moramo razumjeti izazove koje rješava. Godinama je upravljanje stanjem forme u Reactu uključivalo predvidljiv, ali često glomazan obrazac korištenjem useState hooka.

Razmotrimo uobičajeni scenarij: jednostavna forma za dodavanje novog proizvoda na popis. Moramo upravljati s nekoliko dijelova stanja:

Tipična implementacija mogla bi izgledati otprilike ovako:

Primjer: 'Stari način' s višestrukim useState hookovima

// Fiktivna API funkcija
const addProductAPI = async (productName) => {
await new Promise(resolve => setTimeout(resolve, 1500));
if (!productName || productName.length < 3) {
throw new Error('Naziv proizvoda mora imati najmanje 3 znaka.');
}
console.log(`Proizvod "${productName}" je dodan.`);
return { success: true };
};

// Komponenta
import { useState } from 'react';

function OldProductForm() {
const [productName, setProductName] = useState('');
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);

const handleSubmit = async (event) => {
event.preventDefault();
setIsPending(true);
setError(null);

try {
await addProductAPI(productName);
setProductName(''); // Očisti unos nakon uspjeha
} catch (err) {
setError(err.message);
} finally {
setIsPending(false);
}
};

return (
<form onSubmit={handleSubmit}>
<label htmlFor="productName">Naziv proizvoda:</label>
<input
id="productName"
name="productName"
value={productName}
onChange={(e) => setProductName(e.target.value)}
/>
<button type="submit" disabled={isPending}>
{isPending ? 'Dodavanje...' : 'Dodaj proizvod'}
</button>
{error && <p style={{ color: 'red' }}>{error}</p>}
</form>
);
}

Ovaj pristup funkcionira, ali ima nekoliko nedostataka:

Predstavljamo useActionState: Promjena paradigme

useActionState je React hook dizajniran specifično za upravljanje stanjem asinkrone akcije, kao što je slanje forme. On pojednostavljuje cijeli proces povezivanjem stanja izravno s ishodom akcijske funkcije.

Njegov potpis je jasan i sažet:

const [state, formAction] = useActionState(actionFn, initialState);

Analizirajmo njegove komponente:

Praktični primjer: Refaktoriranje s useActionState

Sada, refaktorirajmo našu formu za proizvode koristeći useActionState. Poboljšanje je odmah vidljivo.

Prvo, moramo prilagoditi našu akcijsku logiku. Umjesto bacanja grešaka, akcija bi trebala vratiti objekt stanja koji opisuje ishod.

Primjer: 'Novi način' s useActionState

// Akcijska funkcija, dizajnirana za rad s useActionState
const addProductAction = async (previousState, formData) => {
const productName = formData.get('productName');
await new Promise(resolve => setTimeout(resolve, 1500)); // Simulacija mrežnog kašnjenja

if (!productName || productName.length < 3) {
return { message: 'Naziv proizvoda mora imati najmanje 3 znaka.', success: false };
}

console.log(`Proizvod "${productName}" je dodan.`);
// U slučaju uspjeha, vrati poruku o uspjehu i očisti formu.
return { message: `Uspješno dodan "${productName}"`, success: true };
};

// Refaktorirana komponenta
import { useActionState } from 'react';
// Napomena: U sljedećem odjeljku dodat ćemo useFormStatus za rukovanje stanjem čekanja.

function NewProductForm() {
const initialState = { message: null, success: false };
const [state, formAction] = useActionState(addProductAction, initialState);

return (
<form action={formAction}>
<label htmlFor="productName">Naziv proizvoda:</label>
<input id="productName" name="productName" />
<button type="submit">Dodaj proizvod</button>
{!state.success && state.message && (
<p style={{ color: 'red' }}>{state.message}</p>
)}
{state.success && state.message && (
<p style={{ color: 'green' }}>{state.message}</p>
)}
</form>
);
}

Pogledajte koliko je ovo čišće! Zamijenili smo tri useState hooka jednim useActionState hookom. Odgovornost komponente sada je isključivo renderiranje korisničkog sučelja na temelju `state` objekta. Sva poslovna logika uredno je enkapsulirana unutar `addProductAction` funkcije. Stanje se automatski ažurira na temelju onoga što akcija vrati.

Ali čekajte, što je sa stanjem čekanja? Kako onemogućiti gumb dok se forma šalje?

Rukovanje stanjima čekanja s useFormStatus

React nudi prateći hook, useFormStatus, dizajniran da riješi upravo taj problem. On pruža informacije o statusu posljednjeg slanja forme, ali s ključnim pravilom: mora se pozvati iz komponente koja se renderira unutar <form> elementa čiji status želite pratiti.

Ovo potiče čisto odvajanje odgovornosti. Stvarate komponentu specifično za elemente korisničkog sučelja koji moraju biti svjesni statusa slanja forme, poput gumba za slanje.

useFormStatus hook vraća objekt s nekoliko svojstava, od kojih je najvažnije `pending`.

const { pending, data, method, action } = useFormStatus();

Izrada gumba za slanje svjesnog statusa

Kreirajmo posvećenu `SubmitButton` komponentu i integrirajmo je u našu formu.

Primjer: SubmitButton komponenta

import { useFormStatus } from 'react-dom';
// Napomena: useFormStatus se uvozi iz 'react-dom', a ne iz 'react'.

function SubmitButton() {
const { pending } = useFormStatus();

return (
<button type="submit" disabled={pending}>
{pending ? 'Dodavanje...' : 'Dodaj proizvod'}
</button>
);
}

Sada možemo ažurirati našu glavnu komponentu forme da je koristi.

Primjer: Kompletna forma s useActionState i useFormStatus

import { useActionState } from 'react';
import { useFormStatus } from 'react-dom';

// ... (addProductAction funkcija ostaje ista)

function SubmitButton() { /* ... kao što je definirano gore ... */ }

function CompleteProductForm() {
const initialState = { message: null, success: false };
const [state, formAction] = useActionState(addProductAction, initialState);

return (
<form action={formAction}>
<label htmlFor="productName">Naziv proizvoda:</label>
{/* Možemo dodati key kako bismo resetirali unos nakon uspjeha */}
<input key={state.success ? 'success' : 'initial'} id="productName" name="productName" />
<SubmitButton />
{!state.success && state.message && (
<p style={{ color: 'red' }}>{state.message}</p>
)}
{state.success && state.message && (
<p style={{ color: 'green' }}>{state.message}</p>
)}
</form>
);
}

S ovom strukturom, `CompleteProductForm` komponenta ne treba znati ništa o stanju čekanja. `SubmitButton` je potpuno samostalan. Ovaj kompozicijski obrazac je nevjerojatno moćan za izgradnju složenih, održivih korisničkih sučelja.

Moć progresivnog poboljšanja

Jedna od najdubljih prednosti ovog novog pristupa temeljenog na akcijama, posebno kada se koristi s poslužiteljskim akcijama (Server Actions), je automatsko progresivno poboljšanje. Ovo je ključan koncept za izgradnju aplikacija za globalnu publiku, gdje mrežni uvjeti mogu biti nepouzdani, a korisnici mogu imati starije uređaje ili onemogućen JavaScript.

Evo kako to funkcionira:

  1. Bez JavaScripta: Ako korisnikov preglednik ne izvrši klijentski JavaScript, <form action={...}> radi kao standardna HTML forma. On šalje zahtjev za cijelu stranicu na poslužitelj. Ako koristite framework poput Next.js-a, poslužiteljska akcija se pokreće, a framework ponovno renderira cijelu stranicu s novim stanjem (npr. prikazujući grešku validacije). Aplikacija je potpuno funkcionalna, samo bez glatkoće koju pruža SPA (Single Page Application).
  2. S JavaScriptom: Jednom kada se JavaScript paket učita i React hidrira stranicu, ista `formAction` se izvršava na klijentskoj strani. Umjesto ponovnog učitavanja cijele stranice, ponaša se kao tipičan fetch zahtjev. Akcija se poziva, stanje se ažurira, i samo se potrebni dijelovi komponente ponovno renderiraju.

To znači da logiku forme pišete jednom, a ona besprijekorno radi u oba scenarija. Gradite otpornu, pristupačnu aplikaciju po defaultu, što je ogromna pobjeda za korisničko iskustvo diljem svijeta.

Napredni obrasci i slučajevi upotrebe

1. Poslužiteljske akcije vs. klijentske akcije

`actionFn` koju prosljeđujete useActionState-u može biti standardna klijentska asinkrona funkcija (kao u našim primjerima) ili poslužiteljska akcija (Server Action). Poslužiteljska akcija je funkcija definirana na poslužitelju koja se može izravno pozvati iz klijentskih komponenti. U frameworkovima poput Next.js-a, definirate je dodavanjem direktive "use server"; na vrh tijela funkcije.

Ljepota je u tome što useActionState radi identično s obje vrste akcija. Možete zamijeniti klijentsku akciju poslužiteljskom bez promjene koda komponente.

2. Optimistična ažuriranja s `useOptimistic`

Za još responzivniji osjećaj, možete kombinirati useActionState s useOptimistic hookom. Optimistično ažuriranje je kada ažurirate korisničko sučelje odmah, *pretpostavljajući* da će asinkrona akcija uspjeti. Ako ne uspije, vraćate korisničko sučelje u prethodno stanje.

Zamislite aplikaciju društvenih medija gdje dodajete komentar. Optimistično, prikazali biste novi komentar na popisu odmah dok se zahtjev šalje poslužitelju. useOptimistic je dizajniran da radi ruku pod ruku s akcijama kako bi ovaj obrazac bio jednostavan za implementaciju.

3. Resetiranje forme nakon uspjeha

Čest je zahtjev da se polja forme očiste nakon uspješnog slanja. Postoji nekoliko načina kako to postići s useActionState.

Česte zamke i najbolje prakse

useActionState vs. useReducer: Kratka usporedba

Na prvi pogled, useActionState može se činiti sličnim useReducer-u, jer oba uključuju ažuriranje stanja na temelju prethodnog stanja. Međutim, služe različitim svrhama.

Zaključak: Za slanje formi i asinkrone operacije vezane uz forme, useActionState je moderan, namjenski alat. Za druge složene, klijentske strojeve stanja, useReducer ostaje izvrstan izbor.

Zaključak: Prihvaćanje budućnosti React formi

useActionState hook je više od novog API-ja; on predstavlja temeljnu promjenu prema robusnijem, deklarativnijem i korisnički usmjerenom načinu rukovanja formama i mutacijama podataka u Reactu. Njegovim usvajanjem dobivate:

Kada započinjete nove projekte ili refaktorirate postojeće, razmislite o korištenju useActionState-a. Ne samo da će poboljšati vaše developersko iskustvo čineći vaš kod čišćim i predvidljivijim, već će vas i osnažiti da gradite kvalitetnije aplikacije koje su brže, otpornije i pristupačnije raznolikoj globalnoj publici.