redux-formでFieldレベルのvalidateをやり方と気をつけること

## 環境
- react ^16.3.2
- react-redux ^5.0.7
- redux ^4.0.0
- redux-form ^7.3.0

## FieldレベルでValidate
[Redux Form - Synchronous Validation Example](https://redux-form.com/7.4.2/examples/syncvalidation/)ここでやっているようなValidateの方法もありますが、これだと汎用性に欠けるので僕は専らFieldレベルでValidateするようにしています。

## component
FieldレベルでのValidateを実装する場合には`Field`にvalidateプロパティを渡すだけです。んで、配列になっているのでいくつもValidateメソッドを渡すことができます。
下記の場合であれば必須チェックとメールアドレスチェックですね。

``` javascript
import { Field, reduxForm } from 'redux-form'
import * as Validator from 'utils/validate'

const MyComponet = () => {
  {/** 省略 **/}
}

render() {
  return(
  <Field
    component={MyComponent}
    name="email"
    type="email"
    floatingLabel="ユーザー名"
    textSize={16}
    icon={['fas', 'fa-envelope']}
    validate={[Validator.email, Validator.required]}
  />
  )
}
```

## Validate.js
`utils/Validate.js`とかで別ファイルで作って置くといいですね。
やってることは簡単で正規表現でチェックしてメッセージを出すか`undefined`を`return`します。

`extendValidator`についてはちょっと説明が必要なので後述しますね。

``` javascript
const ErrorMessages = {
  required: '必須項目です。',
  email: '正しいメールアドレスの形式でご入力ください。',
  password: '英字、数字を組み合わせた8文字以上16文字以内で入力。',
  url: 'URLの形式が間違っています。 例：https://example.com'
}

const Regex = {
  email: /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,
  password: /^(?=.*?[a-zA-Z])(?=.*?\d)[!-\~]{8,16}$/,
  url: /^(https?)(:\/\/[-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+)$/
}

export const required = value => (value || typeof value === 'number' ? undefined : ErrorMessages.required)

export const email = value => (value && !Regex.email.test(value) ? ErrorMessages.email : undefined)

export const password = value => (value && !Regex.password.test(value) ? ErrorMessages.password : undefined)

export const url = value => (value && !Regex.url.test(value) ? ErrorMessages.url : undefined)

export const extendValidator = text => func => (func ? text : undefined)
```

## extendValidator
validateメソッドを拡張するメソッドです。なんで拡張する理由があるかというとエラーメッセージを変えたいなどに対応するためです。例えば・・・

ログインページでは必須チェックのメッセージを「必須項目です」ではなく「ログイン用のメールアドレスが入力されていません」といった感じで局所的に変更を加えたい場合などですね。

### 使い方
``` javascript
const exEmail = value => Validator.extendValidator('ちげーし')(Validator.email(value))

validate={[Validator.exEmail]}
```

とは言ってもこれでは拡張メソッドを新たに作る必要はないですよね。validateメソッドに引数で文言を渡せばいいじゃんとなりますもんね。実はredux-formのバージョンが新しくなって例えば`required('ほげ')`とvalidateに渡すことができなくなってしまったんです。なのでどうするかというと拡張メソッドを作るか、下記のような感じでもできます。

``` javascript
export const email = text => value => {
  if (value && !Regex.email.test(value)) {
    return text ? text : ErrorMessages.email
  }
  return undefined
}

validate={[(value) => Validator.email('違うし')(value)]}
```

ちなみにvalidateでは`(value, values, props)`を受け取ることができます。なので、パスワード確認とかも簡単にできます。

``` jsx
<Field
  component={FieldInput}
  name="password"
  type="password"
  validate={[Validator.password, Validator.required]}
  classes={['u-mb-24']}
/>
<Field
  component={FieldInput}
  name="confirm"
  type="password"
  validate={[(value, values) => (
      Validator.confirmPass(value)(values.password)
    ), Validator.required]}
/>
```

``` javascript
export const confirmPass = value => pass => {
  if (value !== pass) {
    return 'パスワードが違います'
  }
  return undefined
}
```