Next.js 15 -fullstack-kehitys (App Router + Server Actions) · Oppitunti

Kenttäkohtaiset validointivirheet useActionStatella

Palauttakaa toiminnoista jäsennellyt validointitulokset ja näyttäkää virheilmoitukset kunkin kentän yhteydessä.

Oppitunti 3/413 vaihetta

Kenttäkohtaiset validointivirheet useActionStatella on ilmainen Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Next.js 15 -fullstack-kehitys (App Router + Server Actions)-kurssilla on yhteensä 4 oppituntia.

Miksi kenttäkohtaiset virheet?

Kun lomakkeen validointi epäonnistuu, käyttäjien on tiedettävä täsmälleen, mikä kenttä on virheellinen ja miksi. Pelkkä ilmoitus "Jokin meni pieleen" pakottaa heidät arvailemaan.

Next.js 15:ssä Server Actions -toiminnot yhdessä useActionState-hookin kanssa mahdollistavat rakenteisen tuloksen palauttamisen palvelimelta ja sisäisen virheilmoituksen näyttämisen heti kunkin syöttökentän alapuolella.

  • email on virheellinen → näyttäkää virhe sähköpostikentän alla
  • password on liian lyhyt → näyttäkää virhe salasanakentän alla

Tässä oppitunnissa näytetään, miten rakenne palautetaan ja näytetään selkeästi.

Toiminnon tilan määrittäminen

Aloittakaa päättämällä, millainen rakenne toiminnon palauttamalla arvolla on. Hyvä käytäntö on säilyttää kenttäkohtaiset virheet kentän nimen mukaan avaimetuissa errors-kartan alkioissa, joissa jokainen arvo on viestitaulukko.

TypeScript-tyypin määrittäminen tekee toiminnon ja komponentin välisestä sopimuksesta selkeän.

export type FieldErrors = {
  email?: string[];
  password?: string[];
};

export type SignupState = {
  errors?: FieldErrors;
  // value the user typed, so we can re-fill the form
  values?: { email?: string };
  // a top-level message for non-field errors
  message?: string;
};

export const initialState: SignupState = {};

Validointi Zodilla toiminnossa

Zodin kaltainen skeemakirjasto tarjoaa sekä validoinnin että valmiin virherakenteen. Kutsukaa safeParse-funktiota, jotta virheet eivät aiheuta poikkeusta – tarkastelkaa sen sijaan palautettua tulosta.

flatten().fieldErrors palauttaa täsmälleen suunnittelemamme muodon { field: string[] }, joten se voidaan yhdistää suoraan tilaamme.

import { z } from 'zod';

const SignupSchema = z.object({
  email: z.string().email('Enter a valid email'),
  password: z.string().min(8, 'At least 8 characters'),
});

const result = SignupSchema.safeParse({
  email: 'not-an-email',
  password: '123',
});

if (!result.success) {
  // { email: ['Enter a valid email'], password: ['At least 8 characters'] }
  console.log(result.error.flatten().fieldErrors);
}

Server Action -toiminnon allekirjoitus

useActionState-hookin kanssa käytettävä toiminto saa kaksi argumenttia: edellisen tilan ja lähetetyn FormData-tiedon. Sen on palautettava seuraava tila.

Merkitkää tiedosto tai funktio 'use server'-direktiivillä. Lukekaa arvot formData.get(...)-kutsulla, validoikaa tiedot ja palauttakaa virheet poikkeuksen heittämisen sijaan.

'use server';

import { z } from 'zod';
import type { SignupState } from './state';

const SignupSchema = z.object({
  email: z.string().email('Enter a valid email'),
  password: z.string().min(8, 'At least 8 characters'),
});

export async function signup(
  prevState: SignupState,
  formData: FormData,
): Promise<SignupState> {
  const email = String(formData.get('email') ?? '');
  const password = String(formData.get('password') ?? '');

  const parsed = SignupSchema.safeParse({ email, password });

  if (!parsed.success) {
    return {
      errors: parsed.error.flatten().fieldErrors,
      values: { email }, // keep email, never echo the password
    };
  }

  // ...persist the user here...
  return { message: 'Account created' };
}

useActionState-hookin kytkeminen

Kutsukaa client-komponentissa useActionState(action, initialState)-hookia. Se palauttaa monikon:

  • state – viimeisin arvo, jonka toiminto palautti
  • formAction – välittäkää tämä lomakkeen action-ominaisuudelle
  • isPending – true, kun toiminto suoritetaan (sopii erinomaisesti painikkeen poistamiseen käytöstä)

Muistakaa 'use client'-direktiivi – hookit toimivat vain client-komponenteissa.

'use client';

import { useActionState } from 'react';
import { signup } from './actions';
import { initialState } from './state';

export function SignupForm() {
  const [state, formAction, isPending] = useActionState(
    signup,
    initialState,
  );

  return (
    <form action={formAction}>
      {/* inputs go here */}
      <button disabled={isPending}>
        {isPending ? 'Creating...' : 'Sign up'}
      </button>
    </form>
  );
}

Sisäisen virheilmoituksen näyttäminen

Näyttääkää kentän virhe lukemalla se arvosta state.errors?.fieldName. Koska kukin kenttä sisältää merkkijonotaulukon, näyttäkää ensimmäinen viesti (tai käykää kaikki viestit läpi).

Käyttäkää optional chaining -syntaksia, jotta ensimmäinen renderöinti – jolloin errors on määrittelemätön – ei aiheuta virhettä.

<div>
  <label htmlFor="email">Email</label>
  <input
    id="email"
    name="email"
    type="email"
    defaultValue={state.values?.email}
  />
  {state.errors?.email && (
    <p className="error">{state.errors.email[0]}</p>
  )}
</div>

Saavutettavuus: virheiden yhdistäminen syöttökenttiin

Ruudunlukijoiden tulee ilmoittaa virhe ja yhdistää se oikeaan kenttään. Kaksi attribuuttia hoitaa tämän pääosin:

  • aria-invalid – asettakaa arvoksi true, kun kentässä on virhe
  • aria-describedby – viitatkaa virhe-elementin tunnisteeseen

Antakaa virhe-elementille pysyvä id ja kohtelias live-alue, jotta päivitykset luetaan ääneen.

<input
  id="email"
  name="email"
  type="email"
  defaultValue={state.values?.email}
  aria-invalid={!!state.errors?.email}
  aria-describedby="email-error"
/>
{state.errors?.email && (
  <p id="email-error" className="error" aria-live="polite">
    {state.errors.email[0]}
  </p>
)}

Käyttäjän syötteen säilyttäminen

Koska lomake renderöidään uudelleen palvelimen tilan perusteella, kontrolloimaton syöttökenttä menettäisi käyttäjän kirjoittaman sisällön. Palauttakaa toiminnosta turvalliset arvot ja välittäkää ne takaisin defaultValue-ominaisuudella.

Älkää koskaan palauttako salasanoja clientille. Palauttakaa vain arkaluonteettomia kenttiä, kuten sähköpostiosoite, jotta käyttäjän ei tarvitse kirjoittaa kaikkea uudelleen validointivirheen jälkeen.

// in the action, on failure:
return {
  errors: parsed.error.flatten().fieldErrors,
  values: { email }, // safe to round-trip
  // password is intentionally omitted
};

// in the component:
<input name="email" defaultValue={state.values?.email} />
<input name="password" type="password" /> {/* always blank */}

Testattava puhdas validointiavustaja

Validointilogiikan ei tarvitse riippua Next.js:stä toimiakseen oikein. Erottakaa se puhtaaksi funktioksi, joka ottaa tavalliset arvot ja palauttaa saman { field: string[] }-virhekartan. Se on helposti yksikkötestattavissa ja suoritettavissa missä tahansa tuomarissa.

type FieldErrors = { email?: string[]; password?: string[] };

function validateSignup(email: string, password: string): FieldErrors {
  const errors: FieldErrors = {};

  const emailOk = /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email);
  if (!emailOk) errors.email = ['Enter a valid email'];

  if (password.length < 8) errors.password = ['At least 8 characters'];

  return errors;
}

const e1 = validateSignup('bad', '123');
console.log(e1); // { email: [...], password: [...] }

const e2 = validateSignup('a@b.co', 'longenough');
console.log(Object.keys(e2).length === 0); // true

console.log(JSON.stringify(validateSignup('x@y.com', 'short')));

Useita virheitä yhtä kenttää kohden

Yksi kenttä voi rikkoa useita sääntöjä samanaikaisesti (tyhjä, liian lyhyt, väärässä muodossa). Koska jokainen alkio on taulukko, voitte näyttää jokaisen viestin luettelona.

Zod kerää luonnostaan useita ongelmia kenttää kohden, joten fieldErrors.password voi sisältää useamman kuin yhden merkkijonon.

{state.errors?.password && (
  <ul className="error-list">
    {state.errors.password.map((msg) => (
      <li key={msg}>{msg}</li>
    ))}
  </ul>
)}

Ylimmän tason ja kenttäkohtaiset virheet

Kaikki virheet eivät liity tiettyyn kenttään. Tietokannasta tuleva päällekkäisen sähköpostiosoitteen ilmoitus tai yleinen palvelinvirhe on lomaketason viesti.

Säilyttäkää molemmat kanavat tilassa: errors kenttäkohtaisia ongelmia varten ja message koko lomaketta koskevaa viestiä varten. Näyttäkää ylimmän tason viesti kenttien yläpuolella, jotta se huomataan.

// in the action, after a successful parse:
try {
  await createUser(email, password);
} catch (err) {
  if (isUniqueViolation(err)) {
    return { errors: { email: ['Email already in use'] } };
  }
  return { message: 'Something went wrong. Please try again.' };
}

// in the component, above the fields:
{state.message && <p role="alert">{state.message}</p>}

Pikatarkistus

Testatkaa, miten hyvin ymmärrätte useActionState-hookin käyttämän toimintojen välisen sopimuksen.

Kertaus

Osaatte nyt toteuttaa kenttäkohtaisen validoinnin Server Actions -toiminnoilla ja useActionState-hookilla:

  • Määritelkää tila käyttämällä errors-karttaa muodossa { field: string[] } sekä valinnaisia values- ja ylimmän tason message-arvoja.
  • Validoikaa toiminnossa safeParse-funktiolla ja palauttakaa flatten().fieldErrors – palauttakaa arvo, älkää koskaan heittäkö poikkeusta.
  • Kytkekää hook kutsulla useActionState(action, initialState) ja välittäkää formAction lomakkeelle; käyttäkää isPending-arvoa painikkeessa.
  • Renderöikää virheet suoraan arvosta state.errors?.field ja käyttäkää saavutettavuutta varten aria-invalid- ja aria-describedby-attribuutteja.
  • Säilyttäkää syöte defaultValue-ominaisuuden avulla, mutta älkää koskaan palauttako salasanoja.
  • Erotelkaa kanavat: kenttäkohtaiset errors-virheet ja koko lomakkeen message-viesti.
Aloita maksutta

Opi TypeScript tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
22
Oppitunnit
88

Usein kysytyt kysymykset

Onko oppitunti ”Kenttäkohtaiset validointivirheet useActionStatella” ilmainen?

Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppimispolun 3 oppituntia, myös oppitunnin “Kenttäkohtaiset validointivirheet useActionStatella”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Next.js 15 -fullstack-kehitys (App Router + Server Actions)-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Kenttäkohtaiset validointivirheet useActionStatella”?

Palauttakaa toiminnoista jäsennellyt validointitulokset ja näyttäkää virheilmoitukset kunkin kentän yhteydessä. Harjoittelet Next.js 15 -fullstack-kehitys (App Router + Server Actions)-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Next.js 15 -fullstack-kehitys (App Router + Server Actions)-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.

Kuinka kauan ”Kenttäkohtaiset validointivirheet useActionStatella”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppitunnilla?

Kyllä. Jokainen Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Progressiivinen parantaminen form Action -propsilla
  2. Odotus- ja lataustilat useFormStatusilla
  3. Kenttäkohtaiset validointivirheet useActionStatella
  4. Välitön palaute useOptimisticilla
← Takaisin: Next.js 15 -fullstack-kehitys (App Router + Server Actions)