LoginSignup
4
4

More than 3 years have passed since last update.

Reactで(少し)動的なFormを作る

Posted at

やりたいこと

  • 上段のプルダウンで値を選ぶと、下段のプルダウンで選べる要素が変わる(制限される)
  • ModalでFormを出して値を任意の数追加する

メモ

  • なぜかFormikのvalidateField()の実行がエラーとなる(issue上がってない・・・)

完成イメージ

下記のような感じ。エリアで関西とかを選ぶと、大阪、兵庫とかしか選べなくなる。
趣味はいくつあるかわからないので、必要数入れてもらう(うらでは配列とかで持つ)。
コードはgithubにあげてみたので、動作確認してからいじったほうがわかりやすいかも。

スクリーンショット 2019-12-19 7.11.12.png

実装

正解があるわけではないが、とりあえず下記のようにしてみた。

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;
4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4