Mục lục

Dynamic Forms: Xử lý Field Array & Nested Data

Làm sao để tạo form 'Thêm người thân', 'Thêm kỹ năng' không giới hạn số lượng? Hướng dẫn sử dụng useFieldArray của React Hook Form.

Bạn gặp yêu cầu: Form "Tạo hóa đơn", user có thể bấm nút + Thêm sản phẩm vô hạn lần. Mỗi sản phẩm có Tên, Số lượng, Đơn giá.

Nếu dùng useState mảng arr[] và tự handle onChange index i -> Cực khó và bug đầy rẫy. React Hook Form tặng bạn vũ khí hạng nặng: useFieldArray.

1. Setup useFieldArray

tsx:
import { useForm, useFieldArray } from "react-hook-form";

function InvoiceForm() {
  const { control, register } = useForm({
    defaultValues: {
      items: [{ name: "Item 1", quantity: 1, price: 0 }] // Giá trị khởi tạo
    }
  });

  const { fields, append, remove } = useFieldArray({
    control,
    name: "items" // Tên field trong object data
  });

  return (
    <ul>
      {fields.map((item, index) => (
        <li key={item.id}> {/* QUAN TRỌNG: Dùng item.id làm key, không dùng index */}
          
          {/* Register dạng nested: items[0].name */}
          <input {...register(`items.${index}.name`)} />
          
          <input type="number" {...register(`items.${index}.quantity`)} />
          
          <button type="button" onClick={() => remove(index)}>Xóa</button>
        </li>
      ))}
      
      <button 
        type="button" 
        onClick={() => append({ name: "", quantity: 1, price: 0 })}
      >
        + Thêm sản phẩm
      </button>
    </ul>
  );
}

2. Tại sao useFieldArray lại tốt hơn?

  1. Unique ID: RHF tự động sinh id (UUID) cho mỗi item. Dùng cái này làm key giúp React không render lại toàn bộ list khi xóa 1 item ở giữa.
  2. Performance: Nó không re-render toàn bộ form khi bạn append item mới.
  3. Drag & Drop: Hỗ trợ hàm move(from, to)swap(A, B) cực tiện để làm tính năng sắp xếp thứ tự.

3. Nested Validation với Zod

Schema Zod cho array cũng rất đơn giản:

ts:
const InvoiceSchema = z.object({
  items: z.array(
    z.object({
      name: z.string().min(1),
      quantity: z.number().min(1),
    })
  ).min(1, "Phải có ít nhất 1 sản phẩm") // Validate độ dài mảng
});

Lỗi sẽ nằm ở errors.items[index].name.

Kết luận

Với useFieldArray, việc xử lý các form danh sách động (Dynamic List) trở nên dễ như ăn kẹo. Mọi logic thêm/sửa/xóa/đổi chỗ đều đã được RHF lo liệu.

Quảng cáo
mdhorizontal