redux-form (3)  - Field-Level Validation Example

[redux-form (1) - Simple Form Example](https://qiita.com/sand/items/5ef1564546c130080398)
[redux-form (2) - Synchronous Validation Example](https://qiita.com/sand/items/7c24d811b8459839f767)
[redux-form (3) - Field-Level Validation Example](https://qiita.com/sand/items/0ce4078ba6fb443f0a54)
[redux-form (4) - Submit Validation Example](https://qiita.com/sand/items/3913526199b509770c6c)
[redux-form (5) - Initialize From State](https://qiita.com/sand/items/24bf124106761b2a8ace)
[redux-form (6) - ユーザ登録](https://qiita.com/sand/items/1ebf763ce98f71af41ec)

---

ReactでForm componentを作るときに、とても便利な**[redux-form](https://redux-form.com/8.2.2/)**の説明です。

**redux-form**の概説についてはまず以下の記事を参考にしてください。

[redux-form (1) - Simple Form Example](https://qiita.com/sand/items/5ef1564546c130080398)



# Field-Level Validation Example


[redux-form (2) - Synchronous Validation Example](https://qiita.com/sand/items/7c24d811b8459839f767)では、Redux state 全体のvaluesを入力として、errorのobjectを返す、validate関数でのチェックの方法を見ました。1個のvalidate関数で全Formのvalidateを行いました。

今回は、各Fieldを個別に validate する方法を紹介します。**各Field毎に、validate propとしてvalidate関数を指定します。validate関数はFieldのvalueを入力にとり、value が valid であればundefineを返し、value が invalid であればエラーメッセージ（文字列）を返します。** 小さく再利用可能なvalidate関数を作ることで、同じようなコードを何回も書くことを避けることができるメリットがあります。


[Field-Level Validation Example](https://redux-form.com/8.2.2/examples/fieldlevelvalidation/) - [Getting Started With redux-form](https://redux-form.com/8.2.2/docs/gettingstarted.md/)


以下のソースコードを見てください。
最初に小さなvalidate関数を多数定義しています。
それをField componentの validate prop に指定します。
例えばrequiredは複数のField componentで指定されています。一度定義すれば、再利用可能なので同じコードを繰り返す必要がなくなります。


```JSX:src/FieldLevelValidationForm.js
import React from 'react'
import { Field, reduxForm } from 'redux-form'

const required = value => (value || typeof value === 'number' ? undefined : 'Required')
const maxLength = max => value =>
  value && value.length > max ? `Must be ${max} characters or less` : undefined
const maxLength15 = maxLength(15)
export const minLength = min => value =>
  value && value.length < min ? `Must be ${min} characters or more` : undefined
export const minLength2 = minLength(2)
const number = value =>
  value && isNaN(Number(value)) ? 'Must be a number' : undefined
const minValue = min => value =>
  value && value < min ? `Must be at least ${min}` : undefined
const minValue13 = minValue(13)
const email = value =>
  value && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)
    ? 'Invalid email address'
    : undefined
const tooYoung = value =>
  value && value < 13
    ? 'You do not meet the minimum age requirement!'
    : undefined
const aol = value =>
  value && /.+@aol\.com/.test(value)
    ? 'Really? You still use AOL for your email?'
    : undefined
const alphaNumeric = value =>
  value && /[^a-zA-Z0-9 ]/i.test(value)
    ? 'Only alphanumeric characters'
    : undefined
export const phoneNumber = value =>
  value && !/^(0|[1-9][0-9]{9})$/i.test(value)
    ? 'Invalid phone number, must be 10 digits'
    : undefined

const renderField = ({
  input,
  label,
  type,
  meta: { touched, error, warning }
}) => (
  <div>
    <label>{label}</label>
    <div>
      <input {...input} placeholder={label} type={type} />
      {touched &&
        ((error && <span>{error}</span>) ||
          (warning && <span>{warning}</span>))}
    </div>
  </div>
)

const FieldLevelValidationForm = props => {
  const { handleSubmit, pristine, reset, submitting } = props
  return (
    <form onSubmit={handleSubmit}>
      <Field
        name="username"
        type="text"
        component={renderField}
        label="Username"
        validate={[required, maxLength15, minLength2]}
        warn={alphaNumeric}
      />
      <Field
        name="email"
        type="email"
        component={renderField}
        label="Email"
        validate={email}
        warn={aol}
      />
      <Field
        name="age"
        type="number"
        component={renderField}
        label="Age"
        validate={[required, number, minValue13]}
        warn={tooYoung}
      />
      <Field
        name="phone"
        type="number"
        component={renderField}
        label="Phone number"
        validate={[required, phoneNumber]}
      />
      <div>
        <button type="submit" disabled={submitting}>
          Submit
        </button>
        <button type="button" disabled={pristine || submitting} onClick={reset}>
          Clear Values
        </button>
      </div>
    </form>
  )
}

export default reduxForm({
  form: 'fieldLevelValidation' // a unique identifier for this form
})(FieldLevelValidationForm)
```


以下は、オリジナルなものを最小化したindex.jsです。


```JSX:src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { createStore, combineReducers } from 'redux'
import { reducer as reduxFormReducer } from 'redux-form'

const dest = document.getElementById('content')
const reducer = combineReducers({
  form: reduxFormReducer // mounted under "form"
})
const store = createStore(reducer)

const showResults = values =>
  new Promise(resolve => {
    setTimeout(() => {
      // simulate server latency
      window.alert(`You submitted:\n\n${JSON.stringify(values, null, 2)}`)
      resolve()
    }, 500)
  })

let render = () => {
  const FieldLevelValidationForm = require('./FieldLevelValidationForm').default
  ReactDOM.hydrate(
    <Provider store={store}>
      <h2>Form</h2>
      <FieldLevelValidationForm onSubmit={showResults} />
    </Provider>,
    dest
  )
}

render()
```



# 実行画面

エラー表示

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/1b166559-aabc-0b06-b25c-67bd96750c26.png)


今回は以上です。