ReactでFormik + Yupを使う最低限（reactstrapを添えて）


## やりたいこと

ReactでFormikを利用する最低限のコードを作りたい（色々発展させる元としたい）。
とりあえず、以下のようのものができる。

![スクリーンショット 2019-09-23 17.42.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55188/e9cd400b-1136-73a0-9122-41addd345a10.png "スクリーンショット 2019-09-23 17.42.01.png")


## 準備

作業場の作成と必要モジュールのインストール。

今回はcreate-react-appを使って雛形を作る。

```
create-react-app formikYup
cd formikYup

npm install --save formik
npm install --save yup
```

## 実装

### App.js

App.jsで同じ階層にあるMyForm.jsを呼び出すようにしている（無駄なコードは削除）。

```js:App.js
import React from 'react';
import MyForm from './MyForm';

function App() {
  return (
    <>
      <MyForm />
    </>
  );
}

export default App;
```

### MyForm.js

いろいろな書き方があるが、いつもReact Nativeで書いているのと（個人的に）同じ書き方にしてみる。
バリデーションにはYupを利用することにする。


```js:MyForm.js
import React from 'react';
import { Formik } from 'formik';
import * as Yup from 'yup';

export default class MyForm extends React.Component {
    render() {
        return (
            <div>
                <h2>My Form</h2>
                <Formik
                    initialValues={{ email: '', password: '' }}
                    onSubmit={(values, actions) => {
                        alert(JSON.stringify(values, null, 2));
                    }}
                    validationSchema={Yup.object().shape({
                        email: Yup.string().email('emailの形式がおかしいです。').required('emailは必須です。'),
                        password: Yup.string().required('パスワードは必須です。'),
                    })}
                >
                    {
                        ({ handleSubmit, handleChange, handleBlur, values, errors, touched }) => (
                            <form onSubmit={handleSubmit}>
                                <input
                                    type="email"
                                    name="email"
                                    onChange={handleChange}
                                    onBlur={handleBlur}
                                    value={values.email}
                                /><br />
                                {touched.email && errors.email ? <div>{errors.email}</div> : null}
                                <input
                                    type="password"
                                    name="password"
                                    onChange={handleChange}
                                    onBlur={handleBlur}
                                    value={values.password}
                                /><br />
                                {touched.password && errors.password ? <div>{errors.password}</div> : null}
                                <button type="submit">Submit</button>
                            </form>
                        )
                    }
                </Formik>
            </div>
        );
    }
}
```

## 応用：Bootstrap4を利用する

基本形？がわかったところでBootstrapを適用してみます。
下記のような完成形を目指します（エラーを起こさせているところ）。


![スクリーンショット 2019-09-23 19.01.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55188/3209d7da-a1ac-ebd7-a119-84e6ec7bda22.png "スクリーンショット 2019-09-23 19.01.37.png")




ReactでBootstrapを使うには[reactstrap](https://reactstrap.github.io/)等のモジュールを利用します。

### インストール

reactstrapにはcssが含まれないため、bootstrapもインストールします。

```bash
npm install --save bootstrap
npm install --save reactstrap
```

### App.js

ここでbootstrapのcssをimportしておきます。
で、別途、MyStrap.jsを作成してimportしています。
特に難しいところはありませんが、エラー表示のところにinvalidやら\<FormFeedback>\</FormFeedback>などを利用しています。



```diff:Appjs
import React from 'react';
import MyForm from './MyForm';
import MyStrap from './MyStrap';
+import 'bootstrap/dist/css/bootstrap.min.css';

function App() {
  return (
    <>
      <MyStrap />
    </>
  );
}

export default App;
```

### MyStrap.js

上記のMyForm.jsをベースにbootstrap化します。

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

export default class MyStrap extends React.Component {
    render() {
        return (
            <div className="max-auto col-8">
                <h2>My Form</h2>
                <Formik
                    initialValues={{ email: '', password: '', gender: '', check: false }}
                    onSubmit={(values, actions) => {
                        alert(JSON.stringify(values, null, 2));
                    }}
                    validationSchema={Yup.object().shape({
                        email: Yup.string().email('emailの形式がおかしいです。').required('emailは必須です。'),
                        password: Yup.string().required('パスワードは必須です。'),
                        gender: Yup.string().oneOf(['male', 'female']).required('選択してください。'),
                        check: Yup.boolean().oneOf([true], '同意してください。'),
                    })}
                >
                    {
                        ({ handleSubmit, handleChange, handleBlur, values, errors, touched }) => (
                            <Form className="text-left" onSubmit={handleSubmit}>
                                {/* email */}
                                <FormGroup className="mb-2">
                                    <Label for="myEmail">Email</Label>
                                    <Input
                                        type="email"
                                        name="email"
                                        id="myEmail"
                                        onChange={handleChange}
                                        onBlur={handleBlur}
                                        value={values.email}
                                        invalid={touched.email && errors.email}
                                    />
                                    <FormFeedback>{errors.email}</FormFeedback>
                                    {/* {touched.email && errors.email ? <div>{errors.email}</div> : null} */}
                                </FormGroup>
                                {/* password */}
                                <FormGroup className="mb-2">
                                    <Label for="myPassword">Password</Label>
                                    <Input
                                        type="password"
                                        name="password"
                                        id="myPassword"
                                        onChange={handleChange}
                                        onBlur={handleBlur}
                                        value={values.password}
                                        invalid={touched.password && errors.password}
                                    />
                                    <FormFeedback>{errors.password}</FormFeedback>
                                    {/* {touched.password && errors.password ? <div>{errors.password}</div> : null} */}
                                </FormGroup>
                                {/* gender redio */}
                                <FormGroup className="mb-2" tag="fieldset">
                                    <p>性別</p>
                                    <FormGroup inline check>
                                        <Label check>
                                            <Input type="radio" name="gender" value="male" onChange={handleChange} />male
                                    </Label>
                                    </FormGroup>
                                    <FormGroup inline check>
                                        <Label check>
                                            <Input type="radio" name="gender" value="female" onChange={handleChange} />female
                                    </Label>
                                    </FormGroup>
                                    <span className="ml-3 text-danger" style={{ fontSize: '0.8rem' }}>
                                        {touched.gender && errors.gender ? <span>{errors.gender}</span> : null}
                                    </span>
                                </FormGroup>
                                {/* checkbox */}
                                <FormGroup check className="mb-2" style={{ marginTop: 30 }}>
                                    <Input type="checkbox" name="check" id="myCheck" onChange={handleChange} />
                                    <Label for="myCheck" check>
                                        同意する
                                    </Label>
                                    <span className="ml-3 text-danger" style={{ fontSize: '0.8rem' }}>
                                        {touched.check && errors.check ? <span>{errors.check}</span> : null}
                                    </span>
                                </FormGroup>
                                {/* Button */}
                                <div style={{ marginTop: 30 }}>
                                    <Button type="submit" color="primary">Submit</Button>
                                </div>
                                
                            </Form>
                        )
                    }
                </Formik>
            </div>
        );
    }
}
```

##　おまけ

上記はreactstrapを利用していますが、react-bootstrapを利用したパータンも下記に追記しておきます。
利用のためには、

```bash
yarn add react-bootstrap bootstrap
```

としておく必要があります。詳しくは[本家サイト](https://react-bootstrap.github.io/getting-started/introduction)をみればいいでしょう。

```js
import React from 'react';
import { Formik } from 'formik';
import * as Yup from 'yup';
import { Form, Button, FormGroup, } from 'react-bootstrap';

export default class SimpleForm extends React.Component {
    render() {
        return (
            <div className="container" style={{ marginTop: 30 }}>
                <div className="mx-auto col-8">
                    <h2>SimpleForm</h2>
                    <Formik
                        initialValues={{ email: '', password: '', area: '', note: '', gender: '', agree: false }}
                        onSubmit={(values) => alert(JSON.stringify(values))}
                        validationSchema={Yup.object().shape({
                            email: Yup.string().email().required(),
                            password: Yup.string().required(),
                            area: Yup.string().oneOf(['関東', '関西', 'その他'], '選択してください。').required(),
                            note: Yup.string().required(),
                            gender: Yup.string().oneOf(['male', 'female']).required('どちらかを選択してください。'),
                            agree: Yup.boolean().oneOf([true], '同意してください。'),
                        })}
                    >
                        {
                            ({ handleSubmit, handleChange, handleBlur, values, errors, touched }) => (
                                <Form onSubmit={handleSubmit}>
                                    <Form.Group>
                                        <Form.Label>Email</Form.Label>
                                        <Form.Control
                                            type="email"
                                            name="email"
                                            onChange={handleChange}
                                            onBlur={handleBlur}
                                            value={values.email}
                                            isInvalid={!!(touched.email && errors.email)}
                                        />
                                        <Form.Control.Feedback type="invalid">
                                            {errors.email}
                                        </Form.Control.Feedback>
                                    </Form.Group>
                                    <Form.Group>
                                        <Form.Label>Password</Form.Label>
                                        <Form.Control
                                            type="password"
                                            name="password"
                                            onChange={handleChange}
                                            onBlur={handleBlur}
                                            value={values.password}
                                            isInvalid={!!(touched.password && errors.password)}
                                        />
                                        <Form.Control.Feedback type="invalid">
                                            {errors.password}
                                        </Form.Control.Feedback>
                                    </Form.Group>

                                    <Form.Group>
                                        <Form.Label>お住まいのエリア</Form.Label>
                                        <Form.Control
                                            as="select"
                                            name="area"
                                            onChange={handleChange}
                                            onBlur={handleBlur}
                                            value={values.area}
                                            isInvalid={!!(touched.area && errors.area)}
                                        >
                                            <option>選択してください</option>
                                            <option>関東</option>
                                            <option>関西</option>
                                            <option>その他</option>
                                        </Form.Control>
                                        <Form.Control.Feedback type="invalid">
                                            {errors.area}
                                        </Form.Control.Feedback>
                                    </Form.Group>

                                    <Form.Group>
                                        <Form.Label>お問合せ内容</Form.Label>
                                        <Form.Control
                                            as="textarea"
                                            rows="5"
                                            name="note"
                                            onChange={handleChange}
                                            onBlur={handleBlur}
                                            value={values.note}
                                            isInvalid={!!(touched.note && errors.note)}
                                        />
                                        <Form.Control.Feedback type="invalid">
                                            {errors.note}
                                        </Form.Control.Feedback>
                                    </Form.Group>

                                    <Form.Group>
                                        <Form.Label>性別</Form.Label>
                                        <Form.Group>
                                            <Form.Check
                                                type="radio"
                                                label="男"
                                                name="gender"
                                                value="male"
                                                inline
                                                onChange={handleChange}
                                                isInvalid={!!errors.gender}
                                            />
                                            <Form.Check
                                                type="radio"
                                                label="女"
                                                name="gender"
                                                value="female"
                                                inline
                                                onChange={handleChange}
                                                isInvalid={!!errors.gender}
                                            />
                                        </Form.Group>
                                        <span className="text-danger" style={{ fontSize: '0.8rem' }}>
                                            {errors.gender}
                                        </span>
                                    </Form.Group>

                                    <Form.Group>
                                        <Form.Label>同意</Form.Label>
                                        <Form.Group>
                                            <Form.Check
                                                type="checkbox"
                                                label="同意する"
                                                name="agree"
                                                value="true"
                                                onChange={handleChange}
                                                isInvalid={!!errors.agree}
                                                feedback={errors.agree}
                                            />
                                        </Form.Group>
                                        {/* <span className="text-danger" style={{ fontSize: '0.8rem' }}>
                                        {errors.agree}
                                    </span> */}
                                    </Form.Group>

                                    {/* button */}
                                    <Button variant="primary" type="submit">Submit</Button>
                                </Form>
                            )
                        }
                    </Formik>
                </div>
            </div>
        );
    }
}

```

## 参考

* https://qiita.com/akameco/items/0151c7c15b7967a147ec
* https://casualdevelopers.com/tech-tips/how-to-use-formik-for-form-in-react/