Help us understand the problem. What is going on with this article?

ReactでFormik + Yupを使う最低限(reactstrapを添えて)

やりたいこと

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

スクリーンショット 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を呼び出すようにしている(無駄なコードは削除)。

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

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

export default App;

MyForm.js

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

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

ReactでBootstrapを使うにはreactstrap等のモジュールを利用します。

インストール

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

npm install --save bootstrap
npm install --save reactstrap

App.js

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

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化します。

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を利用したパータンも下記に追記しておきます。
利用のためには、

yarn add react-bootstrap bootstrap

としておく必要があります。詳しくは本家サイトをみればいいでしょう。

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>
        );
    }
}

参考

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away