Formikで動的バリデーション


## やりたいこと

Formの項目の選択状態により必須・非必須を変えたい。
例えばペットを飼っていれば、ペットの名前を必須としたい。飼ってなければ必須にはしない。

>そもそもInputを消せって話はあるが、裏で値が必須に定義されているとバリデーションを通過できないでエラーになるので、バリデーションをいじる意味はある。

![スクリーンショット 2020-01-06 17.28.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55188/8699e5af-f4c2-88ef-9eb2-b384ca362187.png "スクリーンショット 2020-01-06 17.28.25.png")


## やり方

Yup.lazy()関数というのがあり、値の状況で条件分岐とかできる仕組みを利用すればできるみたい。

```js
validationSchema={Yup.lazy(values => {
if (values.pet === 'noPet') {
    return Yup.object().shape({
    email: Yup.string().required(),
    pet: Yup.string().required(),
    });
} else {
    return Yup.object().shape({
    email: Yup.string().required(),
    pet: Yup.string().required(),
    petName: Yup.string().required(),
    });
}
})}
```


## 実装

formikとかいろいろわかってる想定。

```bash
create-react-app formik-test
cd formik-test

npm install --save bootstrap reactstrap formik yup
```

とか。index.jsでbootstrap.min.css読み込んだりする。

で、サンプルを書く。


```jsx:App.js
import React from 'react';
import './App.css';
import { Form, FormGroup, Label, Input, Button, FormFeedback } from 'reactstrap';
import { Formik } from 'formik';
import * as Yup from 'yup';

class App extends React.Component {

  handleOnSubmit = (values) => {
    alert(JSON.stringify(values));
  }

  render() {
    return (
      <div className="container">
        <h3 className="my-5">動的バリデーションテスト</h3>
        <Formik
          initialValues={{ email: '', pet: '', petName: '' }}
          onSubmit={(values) => this.handleOnSubmit(values)}
          validationSchema={Yup.lazy(values => {
            if (values.pet === 'noPet') {
              return Yup.object().shape({
                email: Yup.string().required(),
                pet: Yup.string().required(),
              });
            } else {
              return Yup.object().shape({
                email: Yup.string().required(),
                pet: Yup.string().required(),
                petName: Yup.string().required(),
              });
            }
          })}
        >
          {
            ({ handleSubmit, handleChange, handleBlur, values, errors, touched, setFieldValue }) => (
              <Form>
                <FormGroup>
                  <Label>Email</Label>
                  <Input
                    type="email"
                    name="email"
                    value={values.email}
                    onChange={handleChange}
                    onBlur={handleBlur}
                    invalid={Boolean(errors.email && touched.email)}
                  />
                  <FormFeedback>
                    {errors.email}
                  </FormFeedback>
                </FormGroup>
                <FormGroup>
                  <legend className="col-form-label">犬を飼っていますか？</legend>
                  <FormGroup inline check>
                    <Label check>
                      飼っている：
                      <Input
                        type="radio"
                        name="pet"
                        value="havePet"
                        onChange={handleChange}
                      />
                    </Label>
                  </FormGroup>
                  <FormGroup inline check>
                    <Label check>
                      飼っていない：
                      <Input
                        type="radio"
                        name="pet"
                        value="noPet"
                        onChange={handleChange}
                      />
                    </Label>
                  </FormGroup>
                  <span className="text-danger small">{touched.pet && errors.pet ? errors.pet : null}</span>
                </FormGroup>
                <FormGroup>
                  <Label>ペットの名前</Label>
                  <Input
                    type="text"
                    name="petName"
                    value={values.petName}
                    onChange={handleChange}
                    onBlur={handleBlur}
                    invalid={Boolean(errors.petName && touched.petName)}
                  />
                  <FormFeedback>
                    {errors.petName}
                  </FormFeedback>
                </FormGroup>
                <FormGroup></FormGroup>
                <Button onClick={handleSubmit}>申し込む</Button>
              </Form>
            )
          }
        </Formik>
      </div>
    );
  }
}

export default App;
```

便利。