React Hook Form – Giải pháp quản lý form cho ReactJS

Khi làm việc với ReactJS chắc hẳn bạn đã từng đau đầu với việc quản lý form sao cho hiệu quả. Form validation là một công việc vô cùng quan trọng, nó giúp cho ứng dụng chúng ta chạy đúng, ngăn cản tấn công của hacker và giúp tăng trải nghiệm người dùng hơn.

Hôm nay mình xin giới thiệu đến mọi người một thư viện quản lý form gọn nhẹ nhưng đầy đủ tính năng cho ReactJS, đó chính là React Hook Form.

Let’s get started !

React Hook Form

React Hook Form là gì?

React Hook Form (RHF) là một thư viện quản lý form cho ReactJS, hiện tại nó đang ở Version 7, github repository xịn xò với 22.8k sao, 1 issues và chỉ nặng 8.3KB (Gzip).

Một vài thư viện đàn anh khác của React hook form cũng rất tuyệt vời như Formik, React final form. Cá nhân mình đã dùng qua Formik và React Hook Form thì mình đánh giá cao RHF hơn vì tính dễ sử dụng, linh động, hỗ trợ hook và yup, … Mặc dù còn vài điểm mà nó chưa tốt được như Formik nhưng mình nghĩ thời gian sắp đến RHF sẽ được hoàn thiện hơn.

Tham khảo: RHF Origin, RHF Github Repo

React Hook Form có gì ?

RHF cung cấp rất nhiều các hook hữu ích, giúp chúng ta dễ dàng quản lý, custom form hơn:

  • useForm: cung cấp những phương thức giúp xử lý form, validate, reset, …
  • useController: giúp chúng ta có thể custom lại một Input component như ý muốn.
  • useFormContext: nó giống như useContext trong ReactJS, giúp chúng ta có thể gửi các dữ liệu qua các biến context.
  • useWatch: giúp theo dõi tính đúng sai của một field nào đó.
  • Ngoài ra còn useFormState và useFieldArray.

Bạn có thể tìm hiểu cụ thể tại RHF API

Đăng ký các field vào RHF

Bước 1: Chuẩn bị một form trong ReactJS. Nhớ sử dụng functional component để dùng Hook nhé.

export default function LoginForm() {
  return (
    <form>
      <div className="field">
        <label>Username: </label>
        <input name="username" />
      </div>

      <div className="field">
        <label>Password: </label>
        <input name="password" />
      </div>

      <div className="field">
        <button type="submit">Đăng nhập</button>
      </div>
    </form>
  );
}

Bước 2: cài đặt React Hook Form

npm install react-hook-form

Bước 3: Đăng ký các field input vào RHF

RHF cung cấp cho chúng ta một hook là useForm với các object và metod như register, handleSubmit, watch, formState: { errors }

register là một hàm dùng để đăng ký các field vào trong RHF. register nhận vào name của field đó và trả về một object = { name, onChange, onBlur, ref }. Lúc này chúng ta không cần thuộc tính name của input nữa vì đã register đã trả ra rồi.

handleSubmit là một hàm dùng để xử lý việc submit form, chặn việc submit form một cách tự nhiên. handleSubmit nhận vào một callback để xử lý, callback này nhận được một tham số là một object chứa giá trị của các filed trong form.

import { useForm } from "react-hook-form";

export default function LoginForm() {
  const {
    register,
    handleSubmit,
    watch,
    formState: { errors }
  } = useForm();

  const onLoginSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onLoginSubmit)}>
      <div className="field">
        <label>Username: </label>
        <input {...register("username")} />
      </div>

      <div className="field">
        <label>Password: </label>
        <input {...register("password")} />
      </div>

      <div className="field">
        <button type="submit">Đăng nhập</button>
      </div>
    </form>
  );
}

Form Validation với RHF

Chúng ta có thể validate một field bất kỳ với RHF bằng việc truyền tham số thứ 2 vào register, tham số này là một object chứa các rules để kiểm tra field đó. Form chỉ có thể submit khi tất cả các field không vi phạm rules nào đã áp dụng. Danh sách các thuộc tính được chấp nhận như sau:

  • required (boolean): field này có bắt buộc điền hay không
  • min (number): giá trị thấp nhất
  • max (number): giá trị lớn nhất
  • minLength (number): độ dài tối thiểu của chuỗi
  • maxLength (number): độ dài tối đa của chuỗi
  • pattern (RegExp): biểu thức chính quy để kiểm tra chuỗi có match hay không
  • validate (function | object): giúp bạn lấy giá trị field, kiểm tra các field phức tạp.
  • Bạn có thể đọc thêm ở đây
// Ví dụ mình có thể thêm validate cho field username như sau
// bắt buộc phải điền và tối đa là 50 ký tự
<input {...register("username", { required: true, maxLength: 50 })} />

RHF validation kết hợp với Yup

Yup là một thư viện của JS giúp chúng ta tạo ra một schema để validate một giá trị một cách nhanh chóng và cực kỳ mạnh mẽ.

Hầu hết các thư viện quản lý form khác đều có thể kết hợp với Yup để dễ dàng quản lý validation schema hơn. Và đương nhiên React Hook Form cũng thế.

Để kết hợp Yup với RHF bạn cần cài yup và @hookform/resolvers

npm install @hookform/resolvers yup

Sau đó, chúng ta sẽ dùng yup và yupResolver của RHF để validate form thay cho cách validate ở trên.

import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";

// tạo một validation schema với yup
const schema = yup.object().shape({
  username: yup
    .string()
    .required("Vui lòng nhập username")
    .max(50, "username tối đa 50 ký tự"),
  password: yup
    .string()
    .required("Vui lòng nhập mật khẩu")
    .max(20, "mật khẩu tối đa 20 ký tự")
});

export default function LoginForm() {
  // sử dụng schema đã tạo ở trên vào RHF
  const { register, handleSubmit, formState: { errors } } = useForm({ resolver: yupResolver(schema) });

  const onLoginSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onLoginSubmit)}>
      <div className="field">
        <label>Username: </label>
        <input {...register("username")} />
      </div>

      <div className="field">
        <label>Password: </label>
        <input {...register("password")} />
      </div>

      <div className="field">
        <button type="submit">Đăng nhập</button>
      </div>
    </form>
  );
}

Xử lý lỗi với RHF

Trước khi tiến hành submit, RHF sẽ validate các giá trị mà chúng ta đã đăng ký qua register. Nếu có bất kỳ field nào không hợp lệ với schema đã áp dụng thì lỗi đó sẽ được lưu vào biến errors do useForm() cung cấp và dĩ nhiên form sẽ không được submit.

Lúc này chúng ta có thể kiểm tra lỗi, xử lý theo ý chúng ta qua biến errors này.

import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";

const schema = yup.object().shape({
  username: yup
    .string()
    .required("Vui lòng nhập username")
    .max(10, "username tối đa 50 ký tự"),
  password: yup
    .string()
    .required("Vui lòng nhập mật khẩu")
    .max(20, "mật khẩu tối đa 20 ký tự")
});

export default function LoginForm() {
  const {
    register,
    handleSubmit,
    formState: { errors }
  } = useForm({ resolver: yupResolver(schema) });

  const onLoginSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onLoginSubmit)}>
      <div className="field">
        <label>Username: </label>
        <input {...register("username")} />
        {/* Nếu có lỗi thì hiển thị nó ra cho người dùng */}
        {errors.username && 
         <p className="error">{errors.username?.message}</p>}
      </div>

      <div className="field">
        <label>Password: </label>
        <input {...register("password")} />
        {/* Nếu có lỗi thì hiển thị nó ra cho người dùng */}
        {errors.password && 
        <p className="error">{errors.password?.message}</p>}
      </div>

      <div className="field">
        <button type="submit">Đăng nhập</button>
      </div>
    </form>
  );
}

Live Demo

Còn nhiều tính năng khác nữa của React Hook Form, mọi nên xem thêm ở trang chủ RHF nhé. Sau đây, mình sẽ demo code nãy giờ tụi mình đã phân tích bên trên, mọi người có thể vào vọc cho vui nhé 😂

Tạm kết

Bài viết này chỉ thế thôi, mình chỉ muốn giới thiệu đến mọi người tổng quan về thư viện quản lý form này. Nếu được mọi người cứ thử qua vài một vài thằng khác như Formik, React-final-form để lựa chọn thư viện phù hợp nhất cho dự án của mình nhé.

Cảm ơn mọi người vì đã đọc bài viết này ❤

Bạn có thể đọc thêm:

Give a Comment