Sügav sukeldumine Reacti `useActionState` hooki. Siit saate teada, kuidas hallata vormi olekuid, käsitleda ootel UI-d ja lihtsustada asünkroonseid toiminguid kaasaegsetes Reacti rakendustes.
Reacti `useActionState` kasutamise meistriklass: Lõplik juhend kaasaegsete vormide ja toimingute käsitlemiseks
Veebiarenduse pidevalt arenevas maastikus jätkab React võimsate tööriistade tutvustamist, mis täpsustavad kasutajaliideste loomist. Üks olulisemaid hiljutisi lisandusi, mis kindlustab selle koha React 19-s, on `useActionState` hook. Varasemalt katsetamisel tuntud kui `useFormState`, on see hook palju enamat kui lihtsalt vormi utiliit; see on põhimõtteline muutus selles, kuidas me hallame asünkroonsete toimingutega seotud olekut.
See põhjalik juhend viib teid aluskontseptsioonidest täiustatud mustriteni, näidates, miks `useActionState` on uuenduslik vahend andmete muutuste, serverisuhtluse ja kasutajate tagasiside käsitlemiseks kaasaegsetes Reacti rakendustes. Ükskõik, kas ehitate lihtsat kontaktivormi või keerukat, andmemahukat juhtpaneeli, selle hooki omandamine lihtsustab teie koodi dramaatiliselt ja parandab kasutajakogemust.
Põhiprobleem: Traditsioonilise toimingute oleku haldamise keerukus
Enne lahendusse süvenemist hindame probleemi. Aastaid hõlmas lihtsa vormi esitamise või API-kõne ümber oleku haldamine ennustatavat, kuid tülikat mustrit, kasutades `useState` ja `useEffect`. Arendajad üle kogu maailma on seda tüüpkoodi lugematuid kordi kirjutanud.
Mõelge standardsele sisselogimisvormile. Me peame haldama:
- Vormi sisendiväärtused (e-post, parool).
- Laadimis- või ooteolek esitamisnuppu keelamiseks ja tagasiside andmiseks.
- Vigade olek serverist pärit sõnumite kuvamiseks (nt "Kehtetud mandaadid").
- Edu olek või andmed edukast esitamisest.
"Enne" näide: `useState` kasutamine
Tüüpiline rakendamine võib välja näha järgmiselt:
// Traditsiooniline lähenemine ilma useActionState'ita
import { useState } from 'react';
// Võlts API funktsioon
async function loginUser(email, password) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (email === 'user@example.com' && password === 'password123') {
resolve({ success: true, message: 'Welcome back!' });
} else {
reject(new Error('Invalid email or password.'));
}
}, 1500);
});
}
function TraditionalLoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = async (event) => {
event.preventDefault();
setIsLoading(true);
setError(null);
try {
const result = await loginUser(email, password);
// Käsitle edukat sisselogimist, nt. ümbersuunamist või edukate sõnumite kuvamist
alert(result.message);
} catch (err) {
setError(err.message);
} finally {
setIsLoading(false);
}
};
return (
);
}
See kood töötab, kuid sellel on mitmeid puudusi:
- Tüüpkood: Toimingute tsükli haldamiseks vajame kolme eraldi `useState` kõnet (`error`, `isLoading` ja iga sisendi jaoks).
- Manuaalne olekuhaldus: Me oleme vastutavad `isLoading` käsitsi true seadmise, seejärel finally blokis false seadmise ja uue esitamise alguses eelnevate vigade tühjendamise eest. See on vigadele vastuvõtlik.
- Sidumine: Esitamise loogika on tihedalt seotud komponendi sündmusehalduriga.
Tutvustus `useActionState`: Lihtsuse paradigmi muutus
`useActionState` on Reacti hook, mis on loodud toimingu oleku haldamiseks. See käsitleb elegantselt ooteoleku, lõpetamise ja vea tsüklit, vähendades tüüpkoodi ja edendades puhtamat, deklaratiivsemat koodi.
Hooki signatuuri mõistmine
Hooki süntaks on lihtne ja võimas:
const [state, formAction] = useActionState(action, initialState);
- `action`: Asünkroonne funktsioon, mis sooritab soovitud toimingu (nt API-kõne, serveritoiming). See võtab vastu eelneva oleku ja mis tahes toiminguspetsiifilised argumendid (nagu vormi andmed) ja peaks tagastama uue oleku.
- `initialState`: `state` väärtus enne, kui toimingut on kunagi täidetud.
- `state`: Praegune olek. See hoiab alguses `initialState` ja pärast toimingu käivitamist hoiab see toimingu tagastatud väärtust. Siin salvestate edukate sõnumite, veateabe või valideerimis tagasiside.
- `formAction`: Teie `action` funktsiooni uus, ümbritsetud versioon. Te edastate selle funktsiooni oma `
"Pärast" näide: Refaktoreerimine koos `useActionState`
Refaktoreerime oma sisselogimisvormi. Pange tähele, kui palju puhtam ja keskendatum komponent muutub.
import { useActionState } from 'react';
import { useFormStatus } from 'react-dom';
// Toimingufunktsioon on nüüd komponendist väljaspool.
// See võtab vastu eelneva oleku ja vormi andmed.
async function loginAction(previousState, formData) {
const email = formData.get('email');
const password = formData.get('password');
// Simuleeri võrgu viivitust
await new Promise(resolve => setTimeout(resolve, 1500));
if (email === 'user@example.com' && password === 'password123') {
return { success: true, message: 'Login successful! Welcome.' };
} else {
return { success: false, message: 'Invalid email or password.' };
}
}
// Eraldi komponent ooteoleku kuvamiseks.
// See on murede eraldamise võtmemuster.
function SubmitButton() {
const { pending } = useFormStatus();
return (
);
}
function ActionStateLoginForm() {
const initialState = { success: false, message: null };
const [state, formAction] = useActionState(loginAction, initialState);
return (
);
}
Parandused on koheselt ilmsed:
- Null manuaalset olekuhaldust: Me ei halli enam ise `isLoading` või `error` olekuid. React haldab seda sisemiselt.
- Lahti ühendatud loogika: `loginAction` funktsioon on nüüd puhas, taaskasutatav funktsioon, mida saab eraldi testida.
- Deklaratiivne UI: Komponendi JSX kuvab deklaratiivselt UI-d vastavalt `state`-le, mida hook tagastab. Kui `state.message` eksisteerib, kuvame selle.
- Lihtsustatud ooteolek: Tutvustasime `useFormStatus`, kaasrakendust, mis muudab ooteoleku haldamise lihtsaks.
`useActionState` peamised omadused ja eelised
1. Sujuv ooteoleku haldamine koos `useFormStatus`-ga
Üks selle mustri võimsamaid omadusi on selle integreerimine `useFormStatus` hookiga. `useFormStatus` pakub teavet vanema `
async function deleteItemAction(prevState, itemId) {
// Simuleeri API kõnet üksuse kustutamiseks
console.log(`Deleting item with ID: ${itemId}`);
await new Promise(res => setTimeout(res, 1000));
const isSuccess = Math.random() > 0.2; // Simuleeri potentsiaalset tõrget
if (isSuccess) {
return { success: true, message: `Item ${itemId} deleted.` };
} else {
return { success: false, message: 'Failed to delete item. Please try again.' };
}
}
function DeletableItem({ id }) {
const [state, deleteAction] = useActionState(deleteItemAction, { message: null });
const [isPending, startTransition] = useTransition();
const handleClick = () => {
startTransition(() => {
deleteAction(id);
});
};
return (
<div>
<span>Item {id}</span>
<button onClick={handleClick} disabled={isPending}>
{isPending ? 'Deleting...' : 'Delete'}
</button>
{state.message && <p>{state.message}</p>}
</div>
);
}
Märkus: Kui `useActionState`-i ei kasutata `
Optimaalsed värskendused koos `useOptimistic`
Veelgi parema kasutajakogemuse saavutamiseks saab `useActionState`-i kombineerida `useOptimistic` hookiga. Optimaalsed värskendused hõlmavad UI kohest värskendamist, *eeldades*, et toiming õnnestub, ja seejärel muudatuse tagasivõtmist ainult siis, kui see ebaõnnestub. See muudab rakenduse tunduvaks vahetuks.
Mõelge lihtsa sõnumite loendi peale. Kui uus sõnum saadetakse, tahame, et see ilmuks kohe loendisse.
import { useActionState, useOptimistic, useRef } from 'react';
async function sendMessageAction(prevState, formData) {
const sentMessage = formData.get('message');
await new Promise(res => setTimeout(res, 2000)); // Simuleeri aeglast võrku
// Tõelises rakenduses oleks see teie API kõne
// Selle demo jaoks eeldame, et see õnnestub alati
return { text: sentMessage, sending: false };
}
function MessageList() {
const formRef = useRef();
const [messages, setMessages] = useState([{ text: 'Hello!', sending: false }]);
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(currentMessages, newMessageText) => [
...currentMessages,
{ text: newMessageText, sending: true }
]
);
const formAction = async (formData) => {
const newMessageText = formData.get('message');
addOptimisticMessage(newMessageText);
formRef.current.reset(); // Vormi visuaalne lähtestamine
const result = await sendMessageAction(null, formData);
// Lõpliku oleku värskendamine
setMessages(current => [...current, result]);
};
return (
<div>
<h2>Chat</h2>
<ul>
{optimisticMessages.map((msg, index) => (
<li key={index} >
{msg.text} {msg.sending && <em>(Sending...)</em>}
</li>
))}
</ul>
<form action={formAction} ref={formRef}>
<input type="text" name="message" placeholder="Your message" />
<button type="submit">Send</button>
</form>
</div>
);
}
Selles keerukamas näites näeme, kuidas `useOptimistic` lisab koheselt sõnumi koos "(Saadab...)" märgistusega. Seejärel käivitatakse `formAction`, et täita tegelik asünkroonne toiming. Kui see on lõpetatud, värskendatakse lõplikku olekut. Kui toiming peaks ebaõnnestuma, viskab React automaatselt ära optimaalse oleku ja taastab algse `messages` oleku.
`useActionState` vs. `useState`: Millal valida kumbagi
Selle uue tööriistaga tekib tavaline küsimus: millal peaksin ikkagi `useState`-i kasutama?
-
Kasutage `useState`-i järgmiste jaoks:
- Puhtalt kliendipoolne, sünkroonne UI olek: Mõelge modaali lülitamisele, vahekaardigrupi praeguse vahekaardi haldamisele või kontrollitud komponendi sisendite haldamisele, mis ei käivita otseselt serveritoimingut.
- Olek, mis ei ole toimingu otsene tulemus: Näiteks filtreerimisseadete salvestamine, mida rakendatakse kliendipoolselt.
- Lihtsad olekumuutujad: Loendur, boolean lipp, string.
-
Kasutage `useActionState`-i järgmiste jaoks:
- Olek, mida värskendatakse vormi esitamise või asünkroonse toimingu tulemusena: See on selle peamine kasutusjuht.
- Kui peate jälgima toimingu ooteoleku, edukuse ja veaoolekuid: See kapseldab kogu selle elutsükli täiuslikult.
- Reacti serveritoimingutega integreerimine: See on serveritoimingutega töötamiseks hädavajalik kliendipoolne hook.
- Vormid, mis nõuavad serveripoolset valideerimist ja tagasisidet: See pakub puhast kanalit, et server saaks tagastada struktureeritud valideerimisvead kliendile.
Ülemaailmsed parimad tavad ja kaalutlused
Ülemaailmse publiku jaoks ehitamisel on oluline arvestada tegureid, mis ületavad koodi funktsionaalsuse.
Ligipääsetavus (a11y)
Vormivigade kuvamisel veenduge, et need oleksid abitehnoloogiate kasutajatele ligipääsetavad. Kasutage dünaamiliste muudatuste väljakuulutamiseks ARIA atribuute.
// Teie vormikomponendis
const { errors } = state;
// ...
<input
name="email"
type="email"
aria-invalid={!!errors?.email}
aria-describedby="email-error"
/>
{errors?.email && (
<p id="email-error" role="alert" style={{ color: 'red' }}>
{errors.email}
</p>
)}
The `aria-invalid="true"` attribute signals to screen readers that the input has an error. The `role="alert"` on the error message ensures it is announced to the user as soon as it appears.
Rahvusvahelise leviku tegur (i18n)
Vältige toimingutest otseselt stringide tagastamist, eriti mitmekeelse rakenduse puhul. Selle asemel tagastage veakoode või võtmeid, mida saab kliendil tõlgitud stringideks teisendada.
// Toiming serveris
async function internationalizedAction(prevState, formData) {
// ...valideerimisloogika...
if (password.length < 8) {
return { success: false, error: { code: 'ERROR_PASSWORD_TOO_SHORT' } };
}
// ...
}
// Komponent kliendil
import { useTranslation } from 'react-i18next';
function I18nForm() {
const { t } = useTranslation();
const [state, formAction] = useActionState(internationalizedAction, {});
return (
<form action={formAction}>
{/* ... sisendid ... */}
{state.error && (
<p role="alert" style={{ color: 'red' }}>
{t(state.error.code)} // Kaardistab 'ERROR_PASSWORD_TOO_SHORT' funktsioonile 'Password must be at least 8 characters long.'
</p>
)}
<SubmitButton />
</form>
);
}
Tüübi ohutus TypeScript-ga
`useActionState`-ga TypeScript-i kasutamine pakub suurepärast tüübi ohutust, püüdes vigu enne nende esinemist kinni. Saate oma toimingu oleku ja koormuse jaoks tüüpe määratleda.
import { useActionState } from 'react';
// 1. Määrake oleku kuju
type FormState = {
success: boolean;
message: string | null;
errors?: {
email?: string;
password?: string;
} | null;
};
// 2. Määrake toimingu funktsiooni signatuur
type SignupAction = (prevState: FormState, formData: FormData) => Promise<FormState>;
const signupAction: SignupAction = async (prevState, formData) => {
// ... rakendamine ...
// TypeScript tagab, et tagastate kehtiva FormState objekti
return { success: false, message: 'Invalid.', errors: { email: '...' } };
};
function TypedSignupForm() {
const initialState: FormState = { success: false, message: null, errors: null };
// 3. Hook tuvastab tüübid õigesti
const [state, formAction] = useActionState(signupAction, initialState);
// Nüüd on `state` täielikult tüübitud. `state.errors.email` kontrollitakse tüübi järgi.
return (
<form action={formAction}>
{/* ... */}
</form>
);
}
Kokkuvõte: Reacti tuleviku olekuhaldus
`useActionState` hook on enam kui lihtsalt mugavus; see esindab Reacti areneva filosoofia põhilist osa. See surub arendajad selgema murede eraldamise poole, vastupidavamate rakenduste poole järkjärgulise täiustamise kaudu ja deklaratiivsema viisi kasutajatoimingute tulemuste haldamiseks.
Toimingu loogika ja selle tulemuse oleku tsentraliseerimisega välistab `useActionState` kliendipoolse tüüpkoodi ja keerukuse märkimisväärse allika. See integreerub sujuvalt `useFormStatus`-ga ooteolekute jaoks ja `useOptimistic`-ga parendatud kasutajakogemuste jaoks, moodustades võimsa trio kaasaegsete andmemuutuste mustrite jaoks.
Uute funktsioonide loomisel või olemasolevate refaktoreerimisel kaaluge `useActionState`-i kasutamist alati, kui hallate olekut, mis on otsene asünkroonse toimingu tulemus. See viib puhtama, vastupidavama ja Reacti tulevase suunaga täielikult kooskõlas oleva koodini.