Formikを使う

## Formikを使う

[jaredpalmer/formik: Build forms in React, without the tears 😭](https://github.com/jaredpalmer/formik)

Formikはいくつかの点で`Redux-Form`より優れている。

readmeによると、

`Redux-Form`でないのかというのと、フォームの状態は本質的に一過性で局所的であるためにreduxでそれを追跡することは不要ため。
さらにredux-fromの場合必ずreducerを通るためアプリケーションの成長につれて描画コストが上がる。
またサイズも半分程度である。
ということが`readme`に書いてある。

まあだいたい同意で、ただのReactコンポーネントでしかのでテストも書きやすい。

## render prop
HoCよりrender propの方がたいていの場合好ましいので、render propで書いていく。

```jsx
function MyForm() {
  return (
    <div>
      <h1>My Form</h1>
      <Formik
        initialValues={{
          email: '',
          password: ''
        }}
        validate={values => {
          let errors = {}
          if (!values.email) {
            errors.email = 'Required'
          } else if (
            !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)
          ) {
            errors.email = 'Invalid email address'
          }
          return errors
        }}
        onSubmit={(values, { setSubmitting, setErrors }) => {
          LoginToMyApp(values).then(
            user => {
              setSubmitting(false)
            },
            errors => {
              setSubmitting(false)
              setErrors(transformMyApiErrors(errors))
            }
          )
        }}
        render={({
          values,
          errors,
          touched,
          handleChange,
          handleBlur,
          handleSubmit,
          isSubmitting
        }) => (
          <form onSubmit={handleSubmit}>
            <input
              type="email"
              name="email"
              onChange={handleChange}
              onBlur={handleBlur}
              value={values.email}
            />
            {touched.email && errors.email && <div>{errors.email}</div>}
            <input
              type="password"
              name="password"
              onChange={handleChange}
              onBlur={handleBlur}
              value={values.password}
            />
            {touched.password &&
              errors.password && <div>{errors.password}</div>}
            <button type="submit" disabled={isSubmitting}>
              Submit
            </button>
          </form>
        )}
      />
    </div>
  )
}
```

ただ、これだと冗長な部分が多い。
Fieldコンポーネントを使うと冗長性をなくせる。
ついでに、validateも外出した方が見通しがよくなる。
また、yupと連携できるが、デフォルトのバリデーションの挙動が非同期になるのでテストでは注意。

```jsx
function validate(values) {
  let errors = {}
  if (!values.email) {
    errors.email = 'Required'
  } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
    errors.email = 'Invalid email address'
  }
  if (!values.password) {
    errors.password = 'Required'
  }
  return errors
}

function MyForm2() {
  return (
    <div>
      <h1>My Form2</h1>
      <Formik
        initialValues={{ email: '', password: '' }}
        validate={validate}
        onSubmit={(values, { setSubmitting, setErrors }) => {
          LoginToMyApp(values).then(
            user => {
              setSubmitting(false)
            },
            errors => {
              setSubmitting(false)
              setErrors(transformMyApiErrors(errors))
            }
          )
        }}
        render={({ values, errors, touched, handleSubmit, isSubmitting }) => (
          <form onSubmit={handleSubmit}>
            <Field type="email" name="email" placeholder="Email" />
            {touched.email && errors.email && <div>{errors.email}</div>}
            <Field type="password" name="password" placeholder="Password" />
            {touched.password &&
              errors.password && <div>{errors.password}</div>}
            <button type="submit" disabled={isSubmitting}>
              Submit
            </button>
          </form>
        )}
      />
    </div>
  )
}
```


また、エラーメッセージの部分が冗長なので、readmeにあるように自分で`ErrorMessage`コンポーネントを定義するとよい。

```jsx
import { Field, getIn } from 'formik';

const ErrorMessage = ({ name }) => (
  <Field
    name={name}
    render={({ form }) => {
      const error = getIn(form.errors, name);
      const touch = getIn(form.touched, name);
      return touch && error ? error : null;
    }}
  />
);
```

これを使うと非常にスッキリする。

```jsx
function MyForm3() {
  return (
    <div>
      <h1>My Form3</h1>
      <Formik
        initialValues={{ email: '', password: '' }}
        validate={validate}
        onSubmit={submit}
        render={({ handleSubmit, isSubmitting }) => (
          <form onSubmit={handleSubmit}>
            <Field type="email" name="email" placeholder="Email" />
            <ErrorMessage name="email" />
            <Field type="password" name="password" placeholder="Password" />
            <ErrorMessage name="password" />
            <button type="submit" disabled={isSubmitting}>
              Submit
            </button>
          </form>
        )}
      />
    </div>
  )
}
```

テストは別記事に書く予定。

## バリデーション
デフォルトでyupと連携しているが、このバリデーションライブラリにはテストがないのでどこまで信用していいのか判断が微妙なところ。
またデフォルトで英語のロケーションが入るのでバリデーションについては検討の余地がある。