React のフォームに Fomik を使って yup でバリデーションした React Select を作る (React Bootstrap 使用)

2022/07/14

バリデーション

zod は TypeScript 前提なので yup にした。

yup

URL

url が strict 過ぎるので。ちなみにこの正規表現だと https://localhost/ のようなパターンも引っかかってしまうので

/((https?):\/\/)?(www.)?[a-z0-9]+((\.[a-z]{2,}){1,3})*(#?\/?[a-zA-Z0-9#]+)*\/?(\?[a-zA-Z0-9-_]+=[a-zA-Z0-9-%]+&?)?$/

こうした。

Formik

どうやら React では普通にフォームは作らず、 Formik 等の力を借りて管理するらしい。幸い、 React Bootstrap + Formik + yup の組み合わせが React Bootstrap 公式ドキュメントにあるのでこれをベースに。

React Select

React Select + Formik パターン。まだ TypeScript に着手できていないのと、今回は Clearable かつ Searchable かつ Multi にしたかったのでサンプルがなく苦戦。

SelectField

import React from 'react';
import CreatableSelect from 'react-select/creatable';

const SelectField = ({
  options,
  field,
  form,
}) => {
    return (
      <CreatableSelect
        isClearable
        isSearchable
        isMulti
        options={options}
        id={field.name}
        name={field.name}
        value={field.value}
        onChange={(option) => {
            return form.setFieldValue(field.name, option);
        }}
        onBlur={field.onBlur}
      />
    )
};

export default SelectField;

Form

import React, { useState } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Form } from 'react-bootstrap';
import SelectField from './SelectField';
import { Formik, Field } from 'formik';
import { object, string, array } from 'yup';

const schema = object({
    categories: array().of(string()).ensure(),
    tags: array().of(string()).ensure()
});

const ComponentForm = () => {
    return (
        <>
            <Formik
                validationSchema={schema}
                onSubmit={console.log}
                initialValues={{
                    categories: categoryArray,
                    tags: tagArray,
                }}
            >
                {({
                    handleSubmit,
                    handleChange,
                    handleBlur,
                    values,
                    touched,
                    isValid,
                    errors,
                }) => {
                    return (
                        <Form noValidate onSubmit={handleSubmit}>
                          <Form.Group className="mb-3" controlId="categories">
                            <Form.Label>
                              <FontAwesomeIcon className="me-2" icon={faFolderTree} />
                              カテゴリ
                            </Form.Label>
                            <Field
                                name="categories"
                                value={elem.categories}
                                options={categoryDatas}
                                component={SelectField}
                            />
                          </Form.Group>
                          <Form.Group className="mb-3" controlId="tags">
                            <Form.Label>
                              <FontAwesomeIcon className="me-2" icon={faTags} />
                              タグ
                            </Form.Label>
                            <Field
                                name="tags"
                                value={elem.tags}
                                options={tagDatas}
                                component={SelectField}
                            />
                          </Form.Group>
                          <Button variant="primary" type="submit">
                              <FontAwesomeIcon className="me-2" icon={faSave} />
                              保存
                          </Button>
                        </Form>
                )}}
            </Formik>
        </>
    );
};

export default ComponentForm;

こんな感じで予期した挙動にすることができた。


Written by Circle
A mound built by the accumulation of hyperlinks are like Kowloon.