Atskleiskite React 'useActionState' hook'o galią. Sužinokite, kaip jis supaprastina formų valdymą, apdoroja laukimo būsenas ir gerina vartotojo patirtį pasitelkiant praktinius, išsamius pavyzdžius.
React useActionState: Išsamus vadovas šiuolaikiniam formų valdymui
Tinklalapių kūrimo pasaulis nuolat vystosi, o React ekosistema yra šių pokyčių priešakyje. Su naujausiomis versijomis React pristatė galingas funkcijas, kurios iš esmės pagerina interaktyvių ir atsparių programų kūrimo būdus. Tarp paveikiausių iš jų yra useActionState hook'as – tikras perversmas formų ir asinchroninių operacijų tvarkyme. Šis hook'as, anksčiau eksperimentinėse versijose žinomas kaip useFormState, dabar yra stabilus ir būtinas įrankis kiekvienam šiuolaikiniam React programuotojui.
Šis išsamus vadovas leis jums nuodugniai susipažinti su useActionState. Išnagrinėsime problemas, kurias jis sprendžia, jo pagrindinę mechaniką ir kaip jį panaudoti kartu su papildomais hook'ais, tokiais kaip useFormStatus, siekiant sukurti geresnę vartotojo patirtį. Nesvarbu, ar kuriate paprastą kontaktų formą, ar sudėtingą, daug duomenų reikalaujančią programą, useActionState supratimas padarys jūsų kodą švaresnį, deklaratyvesnį ir tvirtesnį.
Problema: tradicinio formos būsenos valdymo sudėtingumas
Prieš įvertindami useActionState eleganciją, pirmiausia turime suprasti iššūkius, kuriuos jis sprendžia. Daugelį metų formos būsenos valdymas React sistemoje reikalavo nuspėjamo, bet dažnai gremėzdiško modelio, naudojant useState hook'ą.
Panagrinėkime įprastą scenarijų: paprasta forma, skirta pridėti naują produktą į sąrašą. Mums reikia valdyti kelias būsenos dalis:
- Produkto pavadinimo įvesties reikšmę.
- Įkėlimo arba laukimo būseną, kad vartotojas gautų grįžtamąjį ryšį API iškvietimo metu.
- Klaidos būseną, kad būtų rodomi pranešimai, jei pateikimas nepavyksta.
- Sėkmės būseną arba pranešimą baigus.
Tipiškas įgyvendinimas galėtų atrodyti maždaug taip:
Pavyzdys: „Senasis būdas“ su keliais useState hook'ais
// Fiktyvi API funkcija
const addProductAPI = async (productName) => {
await new Promise(resolve => setTimeout(resolve, 1500));
if (!productName || productName.length < 3) {
throw new Error('Produkto pavadinimas turi būti bent 3 simbolių ilgio.');
}
console.log(`Produktas "${productName}" pridėtas.`);
return { success: true };
};
// Komponentas
{error}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(''); // Sėkmės atveju išvalyti įvesties lauką
} catch (err) {
setError(err.message);
} finally {
setIsPending(false);
}
};
return (
id="productName"
name="productName"
value={productName}
onChange={(e) => setProductName(e.target.value)}
/>
{isPending ? 'Pridedama...' : 'Pridėti produktą'}
{error &&
);
}
Šis metodas veikia, bet turi keletą trūkumų:
- Šabloninis kodas (Boilerplate): Mums reikia trijų atskirų useState iškvietimų, kad valdytume tai, kas konceptualiai yra vienas formos pateikimo procesas.
- Rankinis būsenos valdymas: Programuotojas yra atsakingas už rankinį įkėlimo ir klaidų būsenų nustatymą ir atstatymą teisinga tvarka try...catch...finally bloke. Tai pasikartojantis ir klaidoms imlus procesas.
- Glaudus susiejimas (Coupling): Logika, skirta formos pateikimo rezultatui apdoroti, yra glaudžiai susijusi su komponento atvaizdavimo logika.
Pristatome useActionState: Paradigmos pokytis
useActionState yra React hook'as, specialiai sukurtas valdyti asinchroninio veiksmo, pvz., formos pateikimo, būseną. Jis supaprastina visą procesą, susiedamas būseną tiesiogiai su veiksmo funkcijos rezultatu.
Jo signatūra yra aiški ir glausta:
const [state, formAction] = useActionState(actionFn, initialState);
Išskaidykime jo komponentus:
actionFn(previousState, formData)
: Tai jūsų asinchroninė funkcija, kuri atlieka darbą (pvz., iškviečia API). Ji gauna ankstesnę būseną ir formos duomenis kaip argumentus. Svarbiausia, kad tai, ką ši funkcija grąžina, tampa nauja būsena.initialState
: Tai yra būsenos vertė prieš pirmąjį veiksmo įvykdymą.state
: Tai yra dabartinė būsena. Iš pradžių ji saugo initialState ir po kiekvieno vykdymo atnaujinama į jūsų actionFn grąžintą vertę.formAction
: Tai yra nauja, apgaubta jūsų veiksmo funkcijos versija. Šią funkciją turėtumėte perduoti<form>
elementoaction
atributui. React naudoja šią apgaubtą funkciją, kad sektų laukimo (pending) būseną.
Praktinis pavyzdys: Refaktorizavimas su useActionState
Dabar, refaktorizuokime mūsų produkto formą naudodami useActionState. Pagerėjimas matomas iš karto.
Pirma, turime pritaikyti savo veiksmo logiką. Užuot metusi klaidas, veiksmo funkcija turėtų grąžinti būsenos objektą, kuris apibūdina rezultatą.
Pavyzdys: „Naujasis būdas“ su useActionState
// Veiksmo funkcija, pritaikyta dirbti su useActionState
const addProductAction = async (previousState, formData) => {
const productName = formData.get('productName');
await new Promise(resolve => setTimeout(resolve, 1500)); // Imituojame tinklo delsą
if (!productName || productName.length < 3) {
return { message: 'Produkto pavadinimas turi būti bent 3 simbolių ilgio.', success: false };
}
console.log(`Produktas "${productName}" pridėtas.`);
// Sėkmės atveju grąžiname sėkmės pranešimą.
return { message: `Sėkmingai pridėtas "${productName}"`, success: true };
};
// Refaktorizuotas komponentas
{state.message} {state.message}import { useActionState } from 'react';
// Pastaba: Kitame skyriuje pridėsime useFormStatus, kad tvarkytume laukimo būseną.
function NewProductForm() {
const initialState = { message: null, success: false };
const [state, formAction] = useActionState(addProductAction, initialState);
return (
{!state.success && state.message && (
)}
{state.success && state.message && (
)}
);
}
Pažiūrėkite, kiek tai švariau! Mes pakeitėme tris useState hook'us vienu useActionState hook'u. Komponento atsakomybė dabar yra tik atvaizduoti vartotojo sąsają pagal `state` objektą. Visa verslo logika tvarkingai inkapsuliuota `addProductAction` funkcijoje. Būsena atsinaujina automatiškai, priklausomai nuo to, ką grąžina veiksmas.
Bet palaukite, o kaip dėl laukimo būsenos? Kaip išjungti mygtuką, kol forma yra siunčiama?
Laukimo būsenų tvarkymas su useFormStatus
React pateikia pagalbinį hook'ą, useFormStatus, sukurtą būtent šiai problemai spręsti. Jis suteikia būsenos informaciją apie paskutinį formos pateikimą, tačiau su viena svarbia taisykle: jis turi būti iškviestas iš komponento, kuris yra atvaizduojamas <form>
viduje, kurio būseną norite sekti.
Tai skatina aiškų atsakomybių atskyrimą. Jūs sukuriate komponentą specialiai vartotojo sąsajos elementams, kuriems reikia žinoti apie formos pateikimo būseną, pavyzdžiui, pateikimo mygtukui.
useFormStatus hook'as grąžina objektą su keliomis savybėmis, iš kurių svarbiausia yra `pending`.
const { pending, data, method, action } = useFormStatus();
pending
: loginė reikšmė (boolean), kuri yra `true`, jei tėvinė forma šiuo metu siunčiama, ir `false` kitu atveju.data
: `FormData` objektas, kuriame yra siunčiami duomenys.method
: eilutė, nurodanti HTTP metodą (`'get'` arba `'post'`).action
: nuoroda į funkciją, perduotą formos `action` atributui.
Būseną jaučiančio pateikimo mygtuko sukūrimas
Sukurkime specialų `SubmitButton` komponentą ir integruokime jį į mūsų formą.
Pavyzdys: SubmitButton komponentas
import { useFormStatus } from 'react-dom';
// Pastaba: useFormStatus importuojamas iš 'react-dom', o ne 'react'.
function SubmitButton() {
const { pending } = useFormStatus();
return (
{pending ? 'Pridedama...' : 'Pridėti produktą'}
);
}
Dabar galime atnaujinti mūsų pagrindinį formos komponentą, kad jį naudotų.
Pavyzdys: Pilna forma su useActionState ir useFormStatus
{state.message} {state.message}import { useActionState } from 'react';
import { useFormStatus } from 'react-dom';
// ... (addProductAction funkcija lieka ta pati)
function SubmitButton() { /* ... kaip aprašyta aukščiau ... */ }
function CompleteProductForm() {
const initialState = { message: null, success: false };
const [state, formAction] = useActionState(addProductAction, initialState);
return (
{/* Galime pridėti 'key', kad iš naujo nustatytume įvesties lauką sėkmės atveju */}
{!state.success && state.message && (
)}
{state.success && state.message && (
)}
);
}
Su šia struktūra `CompleteProductForm` komponentui nereikia nieko žinoti apie laukimo būseną. `SubmitButton` yra visiškai savarankiškas. Šis kompozicinis modelis yra neįtikėtinai galingas kuriant sudėtingas, lengvai prižiūrimas vartotojo sąsajas.
Progresyvaus tobulinimo (Progressive Enhancement) galia
Vienas iš didžiausių šio naujo, veiksmais paremto požiūrio privalumų, ypač naudojant jį su Server Actions, yra automatinis progresyvus tobulinimas. Tai gyvybiškai svarbi koncepcija kuriant programas pasaulinei auditorijai, kur tinklo sąlygos gali būti nepatikimos, o vartotojai gali turėti senesnius įrenginius arba išjungtą JavaScript.
Štai kaip tai veikia:
- Be JavaScript: Jei vartotojo naršyklė nevykdo kliento pusės JavaScript,
<form action={...}>
veikia kaip standartinė HTML forma. Ji atlieka viso puslapio užklausą į serverį. Jei naudojate karkasą, pvz., Next.js, serverio pusės veiksmas įvykdomas, ir karkasas iš naujo atvaizduoja visą puslapį su nauja būsena (pvz., rodydamas patvirtinimo klaidą). Programa yra visiškai funkcionali, tik be SPA būdingo sklandumo. - Su JavaScript: Kai JavaScript paketas įkeliamas ir React „hidratuoja“ puslapį, ta pati `formAction` funkcija vykdoma kliento pusėje. Užuot perkrovus visą puslapį, ji elgiasi kaip tipinė „fetch“ užklausa. Veiksmas iškviečiamas, būsena atnaujinama, ir tik reikiamos komponento dalys yra perpiešiamos.
Tai reiškia, kad jūs rašote savo formos logiką vieną kartą, ir ji sklandžiai veikia abiem scenarijais. Jūs pagal nutylėjimą kuriate atsparią, prieinamą programą, o tai yra didžiulis laimėjimas vartotojo patirčiai visame pasaulyje.
Pažangūs modeliai ir panaudojimo atvejai
1. Serverio veiksmai vs. Kliento veiksmai
`actionFn`, kurią perduodate useActionState, gali būti standartinė kliento pusės asinchroninė funkcija (kaip mūsų pavyzdžiuose) arba Serverio veiksmas (Server Action). Serverio veiksmas yra funkcija, apibrėžta serveryje, kurią galima tiesiogiai iškviesti iš kliento komponentų. Karkasuose, tokiuose kaip Next.js, ją apibrėžiate pridėdami "use server";
direktyvą funkcijos kūno viršuje.
- Kliento veiksmai: Idealūs mutacijoms, kurios veikia tik kliento pusės būseną arba tiesiogiai iš kliento kviečia trečiųjų šalių API.
- Serverio veiksmai: Puikiai tinka mutacijoms, kurios apima duomenų bazę ar kitus serverio pusės resursus. Jie supaprastina jūsų architektūrą, pašalindami poreikį rankiniu būdu kurti API galinius taškus kiekvienai mutacijai.
Grožis slypi tame, kad useActionState veikia identiškai su abiem. Galite pakeisti kliento veiksmą serverio veiksmu, nekeisdami komponento kodo.
2. Optimistiniai atnaujinimai su `useOptimistic`
Norėdami dar jautresnės vartotojo sąsajos, galite derinti useActionState su useOptimistic hook'u. Optimistinis atnaujinimas yra tada, kai jūs nedelsiant atnaujinate vartotojo sąsają, *darydami prielaidą*, kad asinchroninis veiksmas pavyks. Jei jis nepavyksta, grąžinate vartotojo sąsają į ankstesnę būseną.
Įsivaizduokite socialinio tinklo programą, kurioje pridedate komentarą. Optimistiškai, jūs iš karto parodytumėte naują komentarą sąraše, kol užklausa siunčiama į serverį. useOptimistic yra sukurtas veikti kartu su veiksmais, kad šį modelį būtų paprasta įgyvendinti.
3. Formos atstatymas sėkmės atveju
Dažnas reikalavimas yra išvalyti formos įvesties laukus po sėkmingo pateikimo. Yra keletas būdų tai pasiekti su useActionState.
- `key` atributo triukas: Kaip parodyta mūsų `CompleteProductForm` pavyzdyje, galite priskirti unikalų `key` įvesties laukui ar visai formai. Kai `key` pasikeičia, React atkabina seną komponentą ir prijungia naują, efektyviai atstatydamas jo būseną. Susieti `key` su sėkmės vėliavėle (`key={state.success ? 'success' : 'initial'}`) yra paprastas ir veiksmingas metodas.
- Valdomi komponentai (Controlled Components): Jei reikia, vis dar galite naudoti valdomus komponentus. Valdydami įvesties reikšmę su useState, galite iškviesti nustatymo funkciją, kad ją išvalytumėte useEffect viduje, kuris klauso sėkmės būsenos iš useActionState.
Dažnos klaidos ir gerosios praktikos
useFormStatus
vieta: Atminkite, kad komponentas, kviečiantis useFormStatus, turi būti atvaizduotas kaip<form>
vaikas. Jis neveiks, jei bus to paties lygio elementas (sibling) ar tėvinis elementas.- Serializuojama būsena: Naudojant Serverio veiksmus, iš veiksmo grąžinamas būsenos objektas turi būti serializuojamas. Tai reiškia, kad jame negali būti funkcijų, `Symbol` ar kitų neserializuojamų verčių. Laikykitės paprastų objektų, masyvų, eilučių, skaičių ir loginių reikšmių.
- Nemeskite klaidų veiksmuose: Užuot `throw new Error()`, jūsų veiksmo funkcija turėtų elegantiškai apdoroti klaidas ir grąžinti būsenos objektą, kuris apibūdina klaidą (pvz., `{ success: false, message: 'Įvyko klaida' }`). Tai užtikrina, kad būsena visada atnaujinama nuspėjamai.
- Apibrėžkite aiškią būsenos struktūrą: Nuo pat pradžių nustatykite nuoseklią savo būsenos objekto struktūrą. Struktūra, tokia kaip `{ data: T | null, message: string | null, success: boolean, errors: Record
| null }`, gali apimti daugelį naudojimo atvejų.
useActionState vs. useReducer: Greitas palyginimas
Iš pirmo žvilgsnio useActionState gali atrodyti panašus į useReducer, nes abu apima būsenos atnaujinimą remiantis ankstesne būsena. Tačiau jie tarnauja skirtingiems tikslams.
useReducer
yra bendrosios paskirties hook'as, skirtas valdyti sudėtingus būsenos perėjimus kliento pusėje. Jis aktyvuojamas siunčiant veiksmus (dispatching actions) ir idealiai tinka būsenos logikai, kuri turi daug galimų, sinchroninių būsenos pokyčių (pvz., sudėtingas kelių žingsnių vedlys).useActionState
yra specializuotas hook'as, skirtas būsenai, kuri keičiasi reaguojant į vieną, paprastai asinchroninį veiksmą. Jo pagrindinis vaidmuo yra integruotis su HTML formomis, Serverio veiksmais ir React konkurentinio atvaizdavimo funkcijomis, tokiomis kaip laukimo būsenos perėjimai.
Išvada: Formų pateikimui ir su formomis susijusioms asinchroninėms operacijoms useActionState yra modernus, specialiai tam sukurtas įrankis. Kitoms sudėtingoms, kliento pusės būsenos mašinoms useReducer išlieka puikus pasirinkimas.
Pabaiga: Priimant React formų ateitį
useActionState hook'as yra daugiau nei tik nauja API; jis atspindi esminį poslinkį link tvirtesnio, deklaratyvesnio ir į vartotoją orientuoto būdo tvarkyti formas ir duomenų mutacijas React sistemoje. Jį pritaikę, jūs gaunate:
- Sumažintą šabloninį kodą: Vienas hook'as pakeičia kelis useState iškvietimus ir rankinį būsenos valdymą.
- Integruotas laukimo būsenas: Sklandžiai tvarkykite įkėlimo vartotojo sąsajas su pagalbiniu useFormStatus hook'u.
- Integruotą progresyvų tobulinimą: Rašykite kodą, kuris veikia su JavaScript arba be jo, užtikrindami prieinamumą ir atsparumą visiems vartotojams.
- Supaprastintą bendravimą su serveriu: Natūraliai dera su Serverio veiksmais, supaprastindamas visos sistemos (full-stack) kūrimo patirtį.
Pradėdami naujus projektus ar refaktorizuodami esamus, apsvarstykite galimybę pasitelkti useActionState. Tai ne tik pagerins jūsų, kaip programuotojo, patirtį, padarydama kodą švaresnį ir labiau nuspėjamą, bet ir suteiks galimybę kurti aukštesnės kokybės programas, kurios yra greitesnės, atsparesnės ir prieinamos įvairiai pasaulinei auditorijai.