やりたいこと
- 上段のプルダウンで値を選ぶと、下段のプルダウンで選べる要素が変わる(制限される)
- ModalでFormを出して値を任意の数追加する
メモ
- なぜかFormikのvalidateField()の実行がエラーとなる(issue上がってない・・・)
完成イメージ
下記のような感じ。エリアで関西とかを選ぶと、大阪、兵庫とかしか選べなくなる。
趣味はいくつあるかわからないので、必要数入れてもらう(うらでは配列とかで持つ)。
コードはgithubにあげてみたので、動作確認してからいじったほうがわかりやすいかも。
実装
正解があるわけではないが、とりあえず下記のようにしてみた。
App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import { Formik, Form as FormikForm, validateYupSchema } from 'formik';
import * as Yup from 'yup';
import {
Form,
FormGroup,
Label,
Input,
FormFeedback,
Button,
Modal,
ModalHeader,
ModalBody,
ModalFooter
} from 'reactstrap';
const areas = [
{ name: '選択してください', value: '選択してください' },
{ name: '関西', value: '関西' },
{ name: '関東', value: '関東' },
{ name: 'その他', value: 'その他' },
];
const prefs = [
{ area: '選択してください', name: '選択してください', value: '選択してください' },
{ area: '関東', name: '東京', value: '東京' },
{ area: '関東', name: '千葉', value: '千葉' },
{ area: '関西', name: '大阪', value: '大阪' },
{ area: '関西', name: '兵庫', value: '兵庫' },
{ area: 'その他', name: '北海道', value: '北海道' },
]
class App extends React.Component {
state = {
modalOpen: false,
hobbies: [],
}
handleSubmit1 = (values) => {
alert(JSON.stringify(values));
}
handleSubmit2 = (values) => {
alert(JSON.stringify(values));
}
handleHobbyAdd = async (hobby) => {
const _hobbies = [...this.state.hobbies];
_hobbies.push(hobby);
await this.setState({ hobbies: _hobbies });
this.handleModalClose();
}
handleModalClose = () => {
this.setState({
modalOpen: false,
});
}
handleModalOpen = () => {
this.setState({
modalOpen: true,
});
}
handleHobbyDelete = (index) => {
// alert(index);
const _hobbies = [...this.state.hobbies];
_hobbies.splice(index, 1);
this.setState({ hobbies: _hobbies });
}
render() {
return (
<div className="container">
<h3 className="my-5 text-center">動的フォームテスト</h3>
<h4 className="my-4">エリア選択フォーム</h4>
<Formik
initialValues={{ area: '選択してください', pref: '選択してください' }}
onSubmit={(values) => this.handleSubmit1(values)}
validationSchema={Yup.object().shape({
area: Yup.string().notOneOf(['選択してください']),
pref: Yup.string().notOneOf(['選択してください']),
})}
>
{
({ handleSubmit, handleChange, handleBlur, values, errors, touched }) => (
<Form onSubmit={handleSubmit}>
<FormGroup>
<Label>エリア</Label>
<Input
type="select"
name="area"
value={values.area}
onChange={handleChange}
onBlur={handleBlur}
invalid={Boolean(touched.area && errors.area)}
>
{
areas.map((area, index) => (
<option value={area.value} key={index}>{area.name}</option>
))
}
</Input>
<FormFeedback>{errors.area}</FormFeedback>
</FormGroup>
<FormGroup>
<Label>都道府県</Label>
<Input
type="select"
name="pref"
value={values.pref}
onChange={handleChange}
onBlur={handleBlur}
invalid={Boolean(touched.pref && errors.pref)}
>
{
(prefs.filter(pref => pref.area === values.area || pref.area === '選択してください')).map((pref, index) => (
<option value={pref.value} key={index}>{pref.name}</option>
))
}
</Input>
<FormFeedback>{errors.pref}</FormFeedback>
</FormGroup>
<div>
<Button type="submit" color="primary">登録</Button>
</div>
</Form>
)
}
</Formik>
<h4 className="my-4">趣味登録フォーム</h4>
<Formik
enableReinitialize
initialValues={{ name: 'hoge', hobby: '', hobbies: this.state.hobbies }}
onSubmit={values => this.handleSubmit2(values)}
validationSchema={Yup.object().shape({
name: Yup.string().required(),
hobbies: Yup.array().min(1),
})}
>
{
({ handleSubmit, handleBlur, handleChange, values, errors, touched, validateField, setFieldValue }) => (
<Form onSubmit={handleSubmit}>
<FormGroup>
<label>氏名</label>
<Input
type="text"
name="name"
value={values.name}
onChange={handleChange}
onBlur={handleBlur}
invalid={Boolean(touched.name && errors.name)}
/>
<FormFeedback>
{errors.name}
</FormFeedback>
</FormGroup>
<FormGroup>
<legend className="col-form-label">趣味</legend>
<div>
<Button type="button" size="sm" color="success" onClick={() => {
// setFieldValue('hobby', '');
this.handleModalOpen();
}}>趣味を追加 +</Button>
{
this.state.hobbies.map((hobby, index) => (
<div className="row container" key={index}>
<Input
type="text"
value={hobby}
disabled
className="col-11 my-3"
/>
<Button
className="col-1 my-3 px-1"
type="button"
onClick={() => this.handleHobbyDelete(index)}
>X</Button>
</div>
))
}
</div>
<p className="text-danger"><small>{touched.hobbies && errors.hobbies ? errors.hobbies : null}</small></p>
</FormGroup>
<Modal isOpen={this.state.modalOpen}>
<ModalHeader toggle={this.handleModalClose}>趣味</ModalHeader>
<ModalBody>
<FormGroup>
<Label>趣味名</Label>
<Input
type="text"
name="hobby"
value={values.hobby}
onChange={handleChange}
onBlur={handleBlur}
invalid={Boolean(touched.hobby && errors.hobby)}
/>
<FormFeedback>{errors.hobby}</FormFeedback>
</FormGroup>
</ModalBody>
<ModalFooter>
<Button type="button" color="info" onClick={() => {
// validateField('name');
// validateField('hobby');
// なぜかvalidateFieldがエラーになる。。。
if (values.hobby === '') {
alert("必須です。")
return;
}
this.handleHobbyAdd(values.hobby);
}}>登録</Button>
<Button type="button" color="secondary" onClick={this.handleModalClose}>キャンセル</Button>
</ModalFooter>
</Modal>
<div>
<Button type="submit" type="submit" color="primary">登録</Button>
</div>
</Form>
)
}
</Formik>
</div>
);
}
}
export default App;