Hallitse Reactin useFormState-hook. Kattava opas suoraviivaiseen lomakkeen tilanhallintaan, palvelinpuolen validointiin ja parempaan käyttökokemukseen Server Actionsien avulla.
React useFormState: Syväsukellus moderniin lomakkeiden hallintaan ja validointiin
Lomakkeet ovat verkon interaktiivisuuden kulmakivi. Yksinkertaisista yhteydenottolomakkeista monimutkaisiin monivaiheisiin ohjattuihin toimintoihin, ne ovat välttämättömiä käyttäjän syötteille ja tietojen lähettämiselle. Vuosien ajan React-kehittäjät ovat navigoineet tilanhallintaratkaisujen maisemassa, joka ulottuu yksinkertaisista useState-hookeista perusskenaarioihin ja tehokkaista kolmannen osapuolen kirjastoista, kuten Formik ja React Hook Form, monimutkaisempiin tarpeisiin. Vaikka nämä työkalut ovat erinomaisia, React kehittyy jatkuvasti tarjotakseen integroidumpia ja tehokkaampia primitiivejä.
Tässä astuu kuvaan useFormState, React 18:ssa esitelty hook. Alun perin suunniteltu toimimaan saumattomasti React Server Actionsien kanssa, useFormState tarjoaa suoraviivaisen, vankan ja natiivin lähestymistavan lomakkeiden tilan hallintaan, erityisesti kun käsitellään palvelinpuolen logiikkaa ja validointia. Se yksinkertaistaa palautteen näyttämistä palvelimelta, kuten validointivirheitä tai onnistumisviestejä, suoraan käyttöliittymässäsi.
Tämä kattava opas vie sinut syväsukellukselle useFormState-hookiin. Tutkimme sen ydinajatuksia, käytännön toteutuksia, edistyneitä malleja ja kuinka se sopii modernin React-kehityksen laajempaan ekosysteemiin. Rakennatpa sovelluksia Next.js:llä, Remixillä tai puhtaalla Reactilla, useFormState:n ymmärtäminen antaa sinulle tehokkaan työkalun parempien ja kestävämpien lomakkeiden rakentamiseen.
Mitä `useFormState` on ja miksi tarvitsemme sitä?
Pohjimmiltaan useFormState on hook, joka on suunniteltu päivittämään tilaa lomaketoiminnon tuloksen perusteella. Ajattele sitä erikoistuneena versiona useReducer:ista, joka on räätälöity erityisesti lomakkeiden lähetyksiä varten. Se siltaa elegantisti kuilun asiakaspuolen käyttäjävuorovaikutuksen ja palvelinpuolen käsittelyn välillä.
Ennen useFormState:a tyypillinen lomakkeen lähetysprosessi, johon liittyy palvelin, saattoi näyttää tältä:
- Käyttäjä täyttää lomakkeen.
- Asiakaspuolen tila (esim.
useState:n avulla) seuraa syötekenttien arvoja. - Lähetyksen yhteydessä tapahtumankäsittelijä (
onSubmit) estää selaimen oletuskäyttäytymisen. fetch-pyyntö rakennetaan manuaalisesti ja lähetetään palvelimen API-päätepisteeseen.- Lataustiloja hallitaan (esim.
const [isLoading, setIsLoading] = useState(false)). - Palvelin käsittelee pyynnön, suorittaa validoinnin ja on vuorovaikutuksessa tietokannan kanssa.
- Palvelin lähettää takaisin JSON-vastauksen (esim.
{ success: false, errors: { email: 'Virheellinen muoto' } }). - Asiakaspuolen koodi jäsentää tämän vastauksen ja päivittää toisen tilamuuttujan näyttääkseen virheet tai onnistumisviestit.
Tämä prosessi, vaikka toimiva, sisältää merkittävän määrän toistuvaa koodia lataustilojen, virhetilojen ja pyyntö/vastaus-syklin hallintaan. useFormState, erityisesti yhdistettynä Server Actionseihin, yksinkertaistaa tätä dramaattisesti luomalla deklaratiivisemman ja integroidumman työnkulun.
useFormState:n tärkeimmät edut ovat:
- Saumaton palvelinintegraatio: Se on natiivi ratkaisu Server Actionsien vastausten käsittelyyn, tehden palvelinpuolen validoinnista ensiluokkaisen osan komponenttiasi.
- Yksinkertaistettu tilanhallinta: Se keskittää lomakkeen tilapäivitysten logiikan, vähentäen useiden
useState-hookien tarvetta datalle, virheille ja lähetystilalle. - Progressiivinen parantaminen:
useFormState:lla ja Server Actionseilla rakennetut lomakkeet voivat toimia, vaikka JavaScript olisi poistettu käytöstä asiakaspuolella, koska ne perustuvat standardeihin HTML-lomakkeiden lähetyksiin. - Parempi käyttökokemus: Se helpottaa välittömän ja kontekstuaalisen palautteen antamista käyttäjälle, kuten sisäisiä validointivirheitä tai onnistumisviestejä, heti lomakkeen lähetyksen jälkeen.
useFormState-hookin allekirjoituksen ymmärtäminen
Hallitaksesi hookin, puretaan ensin sen allekirjoitus ja paluuarvot. Se on yksinkertaisempi kuin miltä se saattaa ensin näyttää.
const [state, formAction] = useFormState(action, initialState);
Parametrit:
action: Tämä on funktio, joka suoritetaan, kun lomake lähetetään. Tämä funktio saa kaksi argumenttia: lomakkeen edellisen tilan ja lähetetyt lomaketiedot. Sen odotetaan palauttavan uuden tilan. Tämä on tyypillisesti Server Action, mutta se voi olla mikä tahansa funktio.initialState: Tämä on arvo, jonka haluat lomakkeen tilan olevan alun perin, ennen kuin yhtään lähetystä on tapahtunut. Se voi olla mikä tahansa sarjallistettava arvo (merkkijono, numero, objekti jne.).
Paluuarvot:
useFormState palauttaa taulukon, jossa on täsmälleen kaksi elementtiä:
state: Lomakkeen nykyinen tila. Ensimmäisellä renderöinnillä tämä on antamasiinitialState. Lomakkeen lähetyksen jälkeen se onaction-funktiosi palauttama arvo. Tätä tilaa käytät käyttöliittymäpalautteen, kuten virheilmoitusten, renderöintiin.formAction: Uusi toimintofunktio, jonka välität<form>-elementtisiaction-propiin. Kun tämä toiminto laukaistaan (lomakkeen lähetyksellä), React kutsuu alkuperäistäaction-funktiotasi edellisellä tilalla ja lomaketiedoilla ja päivittää sittenstate-tilan tuloksella.
Tämä malli saattaa tuntua tutulta, jos olet käyttänyt useReducer:ia. action-funktio on kuin reducer, initialState on alkutila, ja React hoitaa dispatchauksen puolestasi, kun lomake lähetetään.
Käytännön ensiesimerkki: Yksinkertainen tilauslomake
Rakennetaan yksinkertainen uutiskirjeen tilauslomake nähdäksemme useFormState:n toiminnassa. Meillä on yksi sähköpostikenttä ja lähetyspainike. Palvelintoiminto suorittaa perusvalidoinnin tarkistaakseen, onko sähköpostiosoite annettu ja onko se kelvollisessa muodossa.
Määritellään ensin palvelintoimintomme. Jos käytät Next.js:ää, voit sijoittaa tämän samaan tiedostoon komponenttisi kanssa lisäämällä 'use server'; -direktiivin funktion alkuun.
// In actions.js or at the top of your component file with 'use server'
export async function subscribe(previousState, formData) {
const email = formData.get('email');
if (!email) {
return { message: 'Email is required.' };
}
// A simple regex for demonstration purposes
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(email)) {
return { message: 'Please enter a valid email address.' };
}
// Here you would typically save the email to a database
console.log(`Subscribing with email: ${email}`);
// Simulate a delay
await new Promise(res => setTimeout(res, 1000));
return { message: 'Thank you for subscribing!' };
}
Luodaan nyt asiakaskomponentti, joka käyttää tätä toimintoa useFormState:n kanssa.
'use client';
import { useFormState } from 'react-dom';
import { subscribe } from './actions';
const initialState = {
message: null,
};
export function SubscriptionForm() {
const [state, formAction] = useFormState(subscribe, initialState);
return (
<form action={formAction}>
<h3>Subscribe to Our Newsletter</h3>
<div>
<label htmlFor="email">Email Address</label>
<input type="email" id="email" name="email" required />
</div>
<button type="submit">Subscribe</button>
{state?.message && <p>{state.message}</p>}
</form>
);
}
Käydään läpi, mitä tapahtuu:
- Tuomme
useFormState:nreact-dom-paketista (huomaa: eireact-paketista). - Määrittelemme
initialState-objektin. Tämä varmistaa, ettästate-muuttujallamme on johdonmukainen muoto heti ensimmäisestä renderöinnistä alkaen. - Kutsumme
useFormState(subscribe, initialState). Tämä yhdistää komponenttimme tilansubscribe-palvelintoimintoon. - Palautettu
formActionvälitetään<form>-elementinaction-propiin. Tämä on maaginen yhteys. - Renderöimme viestin
state-objektistamme ehdollisesti. Ensimmäisellä renderöinnillästate.messageonnull, joten mitään ei näytetä. - Kun käyttäjä lähettää lomakkeen, React käynnistää
formAction:in. Tämä laukaiseesubscribe-palvelintoimintomme.subscribe-funktio saapreviousState:n (alun perin meidäninitialState) jaformData:n. - Palvelintoiminto suorittaa logiikkansa ja palauttaa uuden tilaobjektin (esim.
{ message: 'Sähköposti vaaditaan.' }). - React vastaanottaa tämän uuden tilan ja renderöi
SubscriptionForm-komponentin uudelleen.state-muuttuja sisältää nyt uuden objektin, ja ehdollinen kappaleemme näyttää virhe- tai onnistumisviestin.
Tämä on uskomattoman tehokasta. Olemme toteuttaneet täyden asiakas-palvelin-validointisyklin minimaalisella asiakaspuolen tilanhallinnan toistokoodilla.
Käyttökokemuksen parantaminen `useFormStatus`-hookilla
Lomakkeemme toimii, mutta käyttökokemus voisi olla parempi. Kun käyttäjä klikkaa 'Tilaa', painike pysyy aktiivisena, eikä ole visuaalista merkkiä siitä, että jotain tapahtuu, ennen kuin palvelin vastaa. Tässä kohtaa useFormStatus-hook astuu kuvaan.
useFormStatus-hook tarjoaa tilatietoja viimeisimmästä lomakkeen lähetyksestä. On ratkaisevan tärkeää, että sitä käytetään komponentissa, joka on <form>-elementin lapsi. Se ei toimi, jos sitä kutsutaan samassa komponentissa, joka renderöi lomakkeen.
Luodaan erillinen SubmitButton-komponentti.
'use client';
import { useFormStatus } from 'react-dom';
export function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Subscribing...' : 'Subscribe'}
</button>
);
}
Nyt voimme päivittää SubscriptionForm-komponenttimme käyttämään tätä uutta komponenttia:
// ... imports
import { SubmitButton } from './SubmitButton';
// ... initialState and other code
export function SubscriptionForm() {
const [state, formAction] = useFormState(subscribe, initialState);
return (
<form action={formAction}>
{/* ... form inputs ... */}
<SubmitButton /> {/* Replace the old button */}
{state?.message && <p>{state.message}</p>}
</form>
);
}
Tämän muutoksen myötä, kun lomake lähetetään, pending-arvo useFormStatus-hookista muuttuu true-arvoon. Meidän SubmitButton-komponenttimme renderöityy uudelleen, poistaen painikkeen käytöstä ja muuttaen sen tekstin muotoon 'Tilataan...'. Kun palvelintoiminto on valmis ja useFormState päivittää tilan, lomake ei ole enää odottavassa tilassa, ja painike palaa alkuperäiseen tilaansa. Tämä antaa olennaista palautetta käyttäjälle ja estää tuplalähetykset.
Edistynyt validointi jäsennellyillä virhetiloilla ja Zodilla
Yksi viestimerkkijono on riittävä yksinkertaisille lomakkeille, mutta todelliset sovellukset vaativat usein kenttäkohtaisia validointivirheitä. Voimme helposti saavuttaa tämän palauttamalla jäsennellymmän tilaobjektin palvelintoiminnostamme.
Parannellaan toimintoamme palauttamaan objekti, jossa on errors-avain, joka puolestaan sisältää viestejä tietyille kentille. Tämä on myös täydellinen tilaisuus esitellä skeeman validointikirjasto, kuten Zod, vankempaa ja ylläpidettävämpää validointilogiikkaa varten.
Vaihe 1: Asenna Zod
npm install zod
Vaihe 2: Päivitä palvelintoiminto
Luomme Zod-skeeman määrittelemään lomaketietojemme odotetun muodon ja validointisäännöt. Sitten käytämme schema.safeParse():a validoimaan saapuva formData.
'use server';
import { z } from 'zod';
// Define the schema for our form
const contactSchema = z.object({
name: z.string().min(2, { message: 'Name must be at least 2 characters.' }),
email: z.string().email({ message: 'Invalid email address.' }),
message: z.string().min(10, { message: 'Message must be at least 10 characters.' }),
});
export async function submitContactForm(previousState, formData) {
const validatedFields = contactSchema.safeParse({
name: formData.get('name'),
email: formData.get('email'),
message: formData.get('message'),
});
// If validation fails, return the errors
if (!validatedFields.success) {
return {
errors: validatedFields.error.flatten().fieldErrors,
message: 'Validation failed. Please check your inputs.',
};
}
// If validation succeeds, process the data
// For example, send an email or save to a database
console.log('Success!', validatedFields.data);
// ... processing logic ...
// Return a success state
return {
errors: {},
message: 'Thank you for your message! We will get back to you soon.',
};
}
Huomaa, kuinka käytämme validatedFields.error.flatten().fieldErrors. Tämä on kätevä Zod-apuohjelma, joka muuntaa virheobjektin käyttökelpoisempaan rakenteeseen, kuten: { name: ['Nimen on oltava vähintään 2 merkkiä pitkä.'], message: ['Viesti on liian lyhyt'] }.
Vaihe 3: Päivitä asiakaskomponentti
Nyt päivitämme lomakekomponenttimme käsittelemään tämän jäsennellyn virhetilan.
'use client';
import { useFormState } from 'react-dom';
import { submitContactForm } from './actions';
import { SubmitButton } from './SubmitButton'; // Assuming we have a submit button
const initialState = {
message: null,
errors: {},
};
export function ContactForm() {
const [state, formAction] = useFormState(submitContactForm, initialState);
return (
<form action={formAction}>
<h2>Contact Us</h2>
<div>
<label htmlFor="name">Name</label>
<input type="text" id="name" name="name" />
{state.errors?.name && (
<p className="error">{state.errors.name[0]}</p>
)}
</div>
<div>
<label htmlFor="email">Email</label>
<input type="email" id="email" name="email" />
{state.errors?.email && (
<p className="error">{state.errors.email[0]}</p>
)}
</div>
<div>
<label htmlFor="message">Message</label>
<textarea id="message" name="message" />
{state.errors?.message && (
<p className="error">{state.errors.message[0]}</p>
)}
</div>
<SubmitButton />
{state.message && <p className="form-status">{state.message}</p>}
</form>
);
}
Tämä malli on uskomattoman skaalautuva ja vankka. Palvelintoiminnostasi tulee ainoa totuuden lähde validointilogiikalle, ja Zod tarjoaa deklaratiivisen ja tyyppiturvallisen tavan määritellä nämä säännöt. Asiakaskomponentista tulee yksinkertaisesti useFormState:n tarjoaman tilan kuluttaja, joka näyttää virheet siellä, minne ne kuuluvat. Tämä vastuualueiden erottelu tekee koodista siistimpää, helpommin testattavaa ja turvallisempaa, koska validointi pakotetaan aina palvelimella.
`useFormState` vs. muut lomakkeiden hallintaratkaisut
Uuden työkalun myötä herää kysymys: "Milloin minun pitäisi käyttää tätä sen sijaan, mitä jo osaan?" Verrataan useFormState:a muihin yleisiin lähestymistapoihin.
`useFormState` vs. `useState`
useStateon täydellinen yksinkertaisiin, vain asiakaspuolen lomakkeisiin tai kun tarvitset monimutkaisia, reaaliaikaisia asiakaspuolen vuorovaikutuksia (kuten live-validointia käyttäjän kirjoittaessa) ennen lähetystä. Se antaa sinulle suoran, yksityiskohtaisen hallinnan.useFormStateloistaa, kun lomakkeen tila määräytyy pääasiassa palvelimen vastauksen perusteella. Se on suunniteltu lomakkeen lähetyksen pyyntö/vastaus-sykliä varten ja on ensisijainen valinta käytettäessä Server Actionseja. Se poistaa tarpeen hallita manuaalisesti fetch-kutsuja, lataustiloja ja vastausten jäsentämistä.
`useFormState` vs. kolmannen osapuolen kirjastot (React Hook Form, Formik)
Kirjastot kuten React Hook Form ja Formik ovat kypsiä, monipuolisia ratkaisuja, jotka tarjoavat kattavan valikoiman työkaluja lomakkeiden hallintaan. Ne tarjoavat:
- Edistynyttä asiakaspuolen validointia (usein skeemaintegraatiolla Zodille, Yupille jne.).
- Monimutkaista tilanhallintaa sisäkkäisille kentille, kenttätaulukoille ja muulle.
- Suorituskykyoptimointeja (esim. uudelleenrenderöintien eristäminen vain muuttuviin syötekenttiin).
- Apuohjelmia controller-komponenteille ja integraatiota käyttöliittymäkirjastojen kanssa.
Joten, milloin valita kumpi?
- Valitse
useFormStatekun:- Käytät React Server Actionseja ja haluat natiivin, integroidun ratkaisun.
- Ensisijainen validoinnin totuuden lähde on palvelin.
- Arvostat progressiivista parantamista ja haluat lomakkeidesi toimivan ilman JavaScriptiä.
- Lomakelogiiikkasi on suhteellisen suoraviivaista ja keskittyy lähetys/vastaus-sykliin.
- Valitse kolmannen osapuolen kirjasto kun:
- Tarvitset laajaa ja monimutkaista asiakaspuolen validointia välittömällä palautteella (esim. validointi on blur- tai on change -tapahtumassa).
- Sinulla on erittäin dynaamisia lomakkeita (esim. kenttien lisääminen/poistaminen, ehdollinen logiikka).
- Et käytä kehystä, jossa on Server Actionseja, ja rakennat omaa asiakas-palvelin-viestintäkerrosta REST- tai GraphQL-API:lla.
- Tarvitset hienojakoista hallintaa suorituskyvystä ja uudelleenrenderöinneistä erittäin suurissa lomakkeissa.
On myös tärkeää huomata, että nämä eivät ole toisiaan poissulkevia. Voit käyttää React Hook Formia hallitsemaan lomakkeesi asiakaspuolen tilaa ja validointia ja sitten käyttää sen lähetyskäsittelijää kutsumaan Server Actionia. Kuitenkin monissa yleisissä käyttötapauksissa useFormState:n ja Server Actionsien yhdistelmä tarjoaa yksinkertaisemman ja elegantimman ratkaisun.
Parhaat käytännöt ja yleiset sudenkuopat
Saadaksesi kaiken irti useFormState:sta, harkitse seuraavia parhaita käytäntöjä:
- Pidä toiminnot fokusoituina: Lomaketoimintosi tulisi olla vastuussa yhdestä asiasta: lomakkeen lähetyksen käsittelystä. Tämä sisältää validoinnin, datan muokkaamisen (tallennus tietokantaan) ja uuden tilan palauttamisen. Vältä sivuvaikutuksia, jotka eivät liity lomakkeen lopputulokseen.
- Määritä johdonmukainen tilan muoto: Aloita aina hyvin määritellyllä
initialState:lla ja varmista, että toimintosi palauttaa aina samanmuotoisen objektin, myös onnistuessa. Tämä estää ajonaikaisia virheitä asiakaspuolella yritettäessä käyttää ominaisuuksia, kutenstate.errors. - Hyödynnä progressiivista parantamista: Muista, että Server Actions toimivat ilman asiakaspuolen JavaScriptiä. Suunnittele käyttöliittymäsi käsittelemään molemmat skenaariot sulavasti. Varmista esimerkiksi, että palvelimella renderöidyt validointiviestit ovat selkeitä, koska käyttäjällä ei ole käytöstä poistetun painikkeen tilan etua ilman JS:ää.
- Erota käyttöliittymän vastuut: Käytä komponentteja, kuten meidän
SubmitButton, kapseloidaksesi tilasta riippuvaista käyttöliittymää. Tämä pitää päälomakekomponenttisi siistimpänä ja noudattaa sääntöä, jonka mukaanuseFormStatus:a on käytettävä lapsikomponentissa. - Älä unohda saavutettavuutta: Kun näytät virheitä, käytä ARIA-attribuutteja, kuten
aria-invalid, syötekentissäsi ja yhdistä virheilmoitukset vastaaviin syötekenttiin käyttämälläaria-describedby:tä varmistaaksesi, että lomakkeesi ovat saavutettavia ruudunlukijoiden käyttäjille.
Yleinen sudenkuoppa: useFormStatus:n käyttäminen samassa komponentissa
Usein tehty virhe on kutsua useFormStatus:a samassa komponentissa, joka renderöi <form>-tagin. Tämä ei toimi, koska hookin on oltava lomakkeen kontekstin sisällä päästäkseen käsiksi sen tilaan. Pura aina se osa käyttöliittymästäsi, joka tarvitsee tilan (kuten painike), omaksi lapsikomponentikseen.
Yhteenveto
useFormState-hook, yhdessä Server Actionsien kanssa, edustaa merkittävää kehitysaskelta siinä, miten käsittelemme lomakkeita Reactissa. Se ohjaa kehittäjiä kohti vankempaa, palvelinkeskeistä validointimallia samalla kun se yksinkertaistaa asiakaspuolen tilanhallintaa. Abstrahoimalla pois lähetyssyklin monimutkaisuudet, se antaa meille mahdollisuuden keskittyä olennaiseen: liiketoimintalogiikan määrittelyyn ja saumattoman käyttökokemuksen rakentamiseen.
Vaikka se ei ehkä korvaa kattavia kolmannen osapuolen kirjastoja jokaisessa käyttötapauksessa, useFormState tarjoaa tehokkaan, natiivin ja progressiivisesti parannetun perustan valtaosalle lomakkeista moderneissa verkkosovelluksissa. Hallitsemalla sen malleja ja ymmärtämällä sen paikan React-ekosysteemissä, voit rakentaa kestävämpiä, ylläpidettävämpiä ja käyttäjäystävällisempiä lomakkeita vähemmällä koodilla ja suuremmalla selkeydellä.