Mục lục

Gửi Form không cần API Route? Server Action là gì?

Cách mạng hóa việc xử lý Form trong Next.js. Viết hàm Backend ngay trong file Frontend. RevalidatePath và Progressive Enhancement.

Trước đây, để làm tính năng "Thêm User", bạn phải:

  1. Tạo file pages/api/create-user.ts.
  2. Ở Component, viết hàm onSubmit -> fetch('/api/create-user', { body: ... }).
  3. Xử lý loading state, error state thủ công.

Giờ đây, bạn có thể viết hàm Backend ngay trong Component.

1. Server Action cơ bản

tsx:
// app/actions.ts
'use server'; // Đánh dấu file này chỉ chạy trên Server

import { revalidatePath } from 'next/cache';
import { db } from '@/lib/db';

export async function createUser(formData: FormData) {
  const name = formData.get('name');
  
  await db.user.create({ data: { name } });
  
  // Quan trọng: Báo cho Next.js biết data đã cũ, cần fetch lại
  revalidatePath('/users'); 
}
tsx:
// app/users/page.tsx
import { createUser } from '@/app/actions';

export default function UserPage() {
  return (
    <form action={createUser}>
      <input name="name" type="text" />
      <button type="submit">Add User</button>
    </form>
  );
}

Điều kỳ diệu: Form này hoạt động ngay cả khi User tắt JavaScript trên trình duyệt! Đây là Progressive Enhancement.

2. useActionState (Xử lý lỗi & Loading)

Trong thực tế, ta cần hiện lỗi Validation (ví dụ: "Tên ngắn quá") và Loading spinner. React 19 cung cấp hook useActionState (trước đây là useFormState).

tsx:
'use client'; // Component này phải là Client để dùng Hook
import { useActionState } from 'react';
import { createUser } from '@/app/actions'; // Action được update để trả về { message: string }

export function UserForm() {
  const [state, formAction, isPending] = useActionState(createUser, { message: '' });

  return (
    <form action={formAction}>
      <input name="name" />
      {/* Disable nút khi đang gửi */}
      <button disabled={isPending}>
        {isPending ? 'Saving...' : 'Add User'}
      </button>

      {/* Hiện lỗi từ Server */}
      <p className="text-red-500">{state?.message}</p>
    </form>
  );
}

3. RevalidatePath vs RevalidateTag

Sau khi Create/Update/Delete thành công, data trên màn hình bị cũ.

  • revalidatePath('/users'): Xóa Cache của trang Users -> Lần tới f5 sẽ có data mới.
  • revalidateTag('user-list'): Xóa Cache của tất cả các fetch request có gắn tag user-list (Mạnh hơn Path).

Kết luận

Server Actions giúp code gọn gàng đến khó tin (Colocation - Code xử lý nằm ngay cạnh Code UI). Bạn không cần quản lý API URL, Content-Type, hay JSON.stringify nữa. Bạn chỉ gọi hàm JS như bình thường, Next.js lo phần RPC (Remote Procedure Call) bên dưới.

Quảng cáo
mdhorizontal