React validate with redux-form

Hiện nay việc validate form ngay khi người dùng nhập dữ liệu là rất phổ biến. Điều này rất thuận tiện bởi vì người dùng có thể nhìn thấy lỗi ngay khi nhập vào form chứ không cần phải đợi đến khi submit form rồi mới nhận được kết quả là dữ liệu họ nhập vào là không đúng. Vậy câu hỏi đặt ra ra làm thế nào để validate form hiệu quả với react hay ta sẽ phải làm thủ công viết validate cho từng trường một. Việc làm thủ công cũng tương đối khả thi và dễ thực hiện đó là đối với những form nhỏ với 1 vài input. Nhưng đối với một form rất lớn với nhiều trường và dữ liệu của nó liên quan tới nhau thì thật là khó!

Dưới đây tôi xin hướng dẫn cách sử dụng một thư viện hỗ trợ cho việc validate realtime khá đơn giản và hữu ích là redux-form

Trong các ví dụ tôi sẽ dùng mã Javascript ES6 nếu bạn dùng ES5 bạn có thể convert sang ES5 tại đây

Để sử dụng redux-form đầu tiên bạn cần cần tạo một reducer để lưu trữ dữ liệu của form vào store.

import { createStore, combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';

const reducers = {
  // ... your other reducers here ...
  form: formReducer
};
const reducer = combineReducers(reducers);
const store = createStore(reducer);

Như vậy là ta đã tạo xong một reducer cho redux-form lưu trữ dữ liệu trong store với key là form. Việc lưu trữ dữ liệu như thế nào hay cấu trúc ra sao có lẽ cũng ko cần chú ý lắm bởi vì ta cũng sẽ ko dùng trực tiếp đến những dữ liệu này. Chúng ta chỉ hiểu đơn giản cần tạo một reducer để lưu trữ dữ liệu của redux-form vào store là đủ. Như vậy đã xong bước khởi tạo môi trường rồi!

Tiếp theo ta sẽ sử dụng redux-form cho với một form đơn giản. Ví dụ như form sign-in chẳng hạn sẽ cần validate usernamepassword

Đầu tiên ta có một component SignIn như sau

import {Component} from 'react';

class SignIn extends Component {
  render() {
    <form>
      <div>
        <label>Username</label>
        <div><input type="text" name="username" placeholder="Username"></div>
      </div>
      <div>
        <label>Username</label>
        <div><input type="text" name="username" placeholder="Username"></div>
      </div>
      <button type="submit">Submit</button>
    </form>
  }
}
export default SignIn;

Để sử dụng redux-form cho component SignIn này ta làm lần lượt như sau.

Thay vì export luôn component SignIn thì ta sẽ tạo ra một redux-form cho SignIn rồi mới export redux-form này ra

import {reduxForm} from 'redux-form';
....
SignIn = reduxForm({
  form: 'SignIn'
})(SignIn)

Như vậy ta đã xong việc tạo redux-form cho một component. Tuy nhiên redux-form lại không quản lí những input field thuần này. Redux-form quản lí các thuộc tính thông qua component Field. Như vậy với những trường input mà cần redux-form quản lí ta cần chuyển sang dùng component Field. Sau khi chuyển ta được code như sau

import {Component} from 'react';
import {reduxForm} from 'redux-form';

class SignIn extends Component {
  render() {
    <form>
      <div>
        <label>Username</label>
        <Field name="username" component={username =>
          <div>
            <input type="text" {...username} placeholder="Username"/>
            {username.touched && username.error && <span>{username.error}</span>}
          </div>
        }/>
      </div>
      <div>
        <label>Username</label>
        <Field name="password" component={password =>
          <div>
            <input type="password" {...password} placeholder="Password"/>
            {password.touched && password.error && <span>{password.error}</span>}
          </div>
        }/>
      </div>
      </div>
      <button type="submit">Submit</button>
    </form>
  }
}

SignIn = reduxForm({
  form: 'SignIn'
})(SignIn)

export default SignIn

Như vậy ta đã xong các bước cơ bản cho form giờ đến bước quan trọng nhất là thêm validate cho form này. Validate đơn giản chỉ là một function với tham số là một object chứa giá trị các giá input trong form (Các input sinh ra bởi component Field và key là thuộc tính name của Field)

...
const validate = values => {
  const errors = {}
  if (!values.username) {
    errors.username = 'Required'
  }

  if (!values.password) {
    errors.email = 'Required'
  }

  return errors
}
...
  <button type="submit" disabled={!this.props.valid}>Submit</button>
...
SignIn = reduxForm({
  form: 'SignIn',
  validate: validate
})(SignIn)

Trong form này ta sẽ validate 2 thuộc tính là usernamepassword. Còn thuộc tính remember me không bắt buộc ta không cần phải validate cũng ko cần dùng component Field cho thuộc tính này. Thêm vào nữa ta sửa lại một chút cho button submit sẽ disable button này khi form không valid. Để kiểm tra tính valid của form chúng ta dùng this.props.valid mỗi khi các thuộc tính của form thay đổi redux-form sẽ cập nhật lại giá trị cho props valid.

Như vậy là ta đã hoàn thành việc validate cho form SignIn khá là đơn giản dưới đây là đoạn code hòan chỉnh

import {Component} from 'react';
import {reduxForm} from 'redux-form';

class SignIn extends Component {
  render() {
    <form>
      <div>
        <label>Username</label>
        <Field name="username" component={username =>
          <div>
            <input type="text" {...username} placeholder="Username"/>
            {username.touched && username.error && <span>{username.error}</span>}
          </div>
        }/>
      </div>
      <div>
        <label>Username</label>
        <Field name="password" component={password =>
          <div>
            <input type="password" {...password} placeholder="Password"/>
            {password.touched && password.error && <span>{password.error}</span>}
          </div>
        }/>
      </div>
      </div>
      <button type="submit" disabled={!this.props.valid}>Submit</button>
    </form>
  }
}

const validate = values => {
  const errors = {}
  if (!values.username) {
    errors.username = 'Required'
  }

  if (!values.password) {
    errors.email = 'Required'
  }

  return errors
}

SignIn = reduxForm({
  form: 'SignIn',
  validate: validate
})(SignIn)

export default SignIn

Như vậy ta đã hoàn thành việc validate cho một form đơn giản. Trong bài tiếp thôi tôi sẽ viết cho các trường hợp phức tạp hơn như nested form, khởi tạo (set lại) dữ liệu cho form, validate sau khi nhận kết quả của ajax request.

Cảm ơn bạn đã theo dõi bài viết!