nextjsserver-actionsformstateMenengah4 mnt baca

useActionState

Mengelola state hasil action: error validasi dan pesan sukses.

Masalah: form yang amnesia

Di modul sebelumnya, form kita bisa menampilkan pesan hasil, tapi logikanya menempel di fungsi inline. Bayangkan form pendaftaran dengan lima field dan pesan error berbeda per field, ditambah pesan sukses setelah berhasil. Mengelola semua itu dengan useState manual cepat berubah jadi spageti. useActionState adalah jawaban React: ia menghubungkan return value Server Action langsung menjadi state, sehingga alur "submit, proses, tampilkan hasil" menjadi satu jalur yang rapi.

Hook ini mengembalikan tiga hal: state (hasil terakhir action), formAction (fungsi yang dipasang ke <form action={...}>), dan isPending (status loading, jadi kamu tidak butuh useFormStatus terpisah).

Action dua parameter

Action yang dipakai useActionState punya signature khusus: ia menerima state sebelumnya sebagai parameter pertama, lalu FormData sebagai parameter kedua, dan me-return state baru:

tsx
// app/daftar/actions.ts
"use server";

type State = {
  error: string | null;
  fieldErrors?: { email?: string; password?: string };
  sukses?: boolean;
};

export async function daftar(_prev: State, formData: FormData): Promise<State> {
  const email = String(formData.get("email") ?? "");
  const password = String(formData.get("password") ?? "");

  const fieldErrors: State["fieldErrors"] = {};
  if (!email.includes("@")) fieldErrors.email = "Format email tidak valid";
  if (password.length < 8) fieldErrors.password = "Password minimal 8 karakter";
  if (Object.keys(fieldErrors).length > 0) {
    return { error: "Periksa kembali isianmu", fieldErrors };
  }

  if (await sudahTerdaftar(email)) {
    return { error: "Email sudah terdaftar, coba login saja" };
  }

  await simpanPengguna(email, password);
  return { error: null, sukses: true };
}

Parameter pertama (_prev) jarang dipakai langsung, tapi ia ada agar kamu bisa membangun state inkremental, misalnya mengakumulasi daftar error dari beberapa submit.

Form yang membaca state

tsx
// app/daftar/Form.tsx
"use client";
import { useActionState } from "react";
import { daftar } from "./actions";

const awal = { error: null as string | null };

export function Form() {
  const [state, formAction, isPending] = useActionState(daftar, awal);

  if (state.sukses) {
    return <p>Akun berhasil dibuat! Cek email untuk verifikasi.</p>;
  }

  return (
    <form action={formAction}>
      <input name="email" type="email" placeholder="Email" />
      {state.fieldErrors?.email && <p className="error">{state.fieldErrors.email}</p>}
      <input name="password" type="password" placeholder="Password" />
      {state.fieldErrors?.password && <p className="error">{state.fieldErrors.password}</p>}
      {state.error && <p className="error">{state.error}</p>}
      <button disabled={isPending}>{isPending ? "Mendaftar..." : "Daftar"}</button>
    </form>
  );
}

Perhatikan betapa deklaratifnya: tidak ada try/catch, tidak ada setState manual. Setiap submit me-replace state dengan return value terbaru dari server. Error per field tampil tepat di bawah input yang bermasalah, persis seperti form profesional.

Satu pola lanjutan yang layak kamu tahu: karena state bertahan antar submit, kamu bisa menampilkan pesan sukses sementara lalu me-resetnya, atau menumpuk riwayat aksi. Kuncinya selalu sama, state adalah cermin jujur dari apa yang terakhir dikembalikan server.

Catatan teknis: Bentuk state bebas, tapi harus konsisten dan serializable. Jangan me-return Error instance atau Date, karena data ini dikirim lewat network sebagai JSON. Dan ingat: useActionState butuh React 19, pastikan Next.js-mu versi 15 ke atas.

Tantangan

Form dengan pesan

Buat form yang menampilkan pesan error dari server (mis. email duplikat) DAN pesan sukses setelah berhasil, memakai useActionState.