やりたいこと
ReactでFormikを利用する最低限のコードを作りたい(色々発展させる元としたい)。
とりあえず、以下のようのものができる。
準備
作業場の作成と必要モジュールのインストール。
今回はcreate-react-appを使って雛形を作る。
create-react-app formikYup
cd formikYup
npm install --save formik
npm install --save yup
実装
App.js
App.jsで同じ階層にあるMyForm.jsを呼び出すようにしている(無駄なコードは削除)。
import React from 'react';
import MyForm from './MyForm';
function App() {
return (
<>
<MyForm />
</>
);
}
export default App;
MyForm.js
いろいろな書き方があるが、いつもReact Nativeで書いているのと(個人的に)同じ書き方にしてみる。
バリデーションにはYupを利用することにする。
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を適用してみます。
下記のような完成形を目指します(エラーを起こさせているところ)。
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>などを利用しています。
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化します。
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>
);
}
}