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

Formikでreact-bootstrapを使いたい

はじめに

先日react-redux-fromからFormikに切り換えたのですが、普段使いのUI Frameworkはreact-bootstrapなんです。Formikの公式を見ると、未対応じゃないですかreactstrapっていつの間にかBootstrap4に対応していたんですね…。知らなかった…。ドキュメントとか不親切だからreact-bootstrapの方が良いと思うんですけどね。

仕方がないので

自分でコンポーネントを作ることにしました。
希望する機能としては以下の通り。

  • Input Groupに対応
  • <Field name="name" component={Text} />よりも <Text name="name" />のような使い方がしたい。
  • onChange onBlurをハンドリング

とりあえず作ってみる

inputField.js
import React from 'react';
import { InputGroup, Form } from 'react-bootstrap';
import { Field } from 'formik';

export const Text = ({
  prependText,
  appendText,
  name,
  ...props
}) => (
  <Field name={name} {...props}>
    {({
      field, 
      meta,
    }) => {
      return (
        <>
          <InputGroup>
            {prependText && (
              <InputGroup.Prepend>
                <InputGroup.Text> {prependText} </InputGroup.Text>
              </InputGroup.Prepend>
            )}
            <Form.Control
              type="text"
              {...props}
              {...field}
              isInvalid={meta.touched && meta.error}
            />
            {appendText && (
              <InputGroup.Append>
                <InputGroup.Text> {appendText} </InputGroup.Text>
              </InputGroup.Append>
            )}
            {meta.touched && meta.error && (
              <Form.Control.Feedback type="invalid">
                {meta.error}
              </Form.Control.Feedback>
             )}
          </InputGroup>
        </>
      );
    }}
  </Field>
);

上記、一件良さそうに見えます。しかも公式が推奨している書き方だし。でも、InputGroupを使うとコンポーネントの角が出てイケてないんです。こんな風になる。
スクリーンショット 2020-03-08 11.35.05.png

対策として以下の書き方をします。

<InputGroup>
  {prependText && (
    <InputGroup.Prepend>
      <InputGroup.Text> {prependText} </InputGroup.Text>
    </InputGroup.Prepend>
  )}
  <Form.Control
    type="text"
    {...props}
    {...field}
    onChange={handleChange}
    onBlur={handleBlur}
    isInvalid={meta.touched && meta.error}
  />
  {appendText && (
    <InputGroup.Append>
      <InputGroup.Text> {appendText} </InputGroup.Text>
    </InputGroup.Append>
  )}
</InputGroup>
{meta.touched && meta.error && (
  <InputGroup>
    <div className="invalid-feedback d-block">{meta.error}</div>
  </InputGroup>
)}

Formコンポーネントを使うのを止め、別のInputGroupに出して className="d-block"を使ってinvalid-feedbackクラスの display:noneを上書きします。これにより理想の出力となります。公式も修正した方が良いと思うんですけどね。

使うときはこんな感じ

<Text name="zipcode" type="tel" prependText="" />

以下のように表示されます。上手くいきました。
スクリーンショット 2020-03-08 12.25.39.png

onChange onBlurに対応させる

<Field />はデフォルトで onChange onBlurをフックしちゃうので一手間かけます。

export const Text = ({
  onChange,
  onBlur,
 /* 省略 */
}) => (
  <Field name={name} {...props}>
    {({
      field,
      meta,
    }) => {
      const handleChange = event => {
        if (onChange) {
          onChange(field.name, event.target.value);
        }
        field.onChange(field.name)(event);
      };
      const handleBlur = event => {
        if (onBlur) {
          onBlur(field.name, event.target.value);
        }
        field.onBlur(field.name)(event);
      };
      return (
         /* 省略 */
            <Form.Control
              type="text"
              {...props}
              {...field}
              onChange={handleChange}
              onBlur={handleBlur}
              isInvalid={meta.touched && meta.error}/>
         /* 省略 */
      );
    }}
  </Field>
);

これにより

import { Text } from '/path/to/inputFiled.js';

const handleChange = (fieldName,value ) => {

};

<Text onChange={(fieldName,value)=>handleChange(fieldName,value)} />

のような使い方ができます。まあ、onChangeが無くても Yupの拡張で何とかなるんですが。

発展

他にも

  • コンポーネント内のhandleBlurに記述を追加する事によって入力値の変換が可能になる。 <Katakana />コンポーネントとかが作成できます。handleChangeには入れない方が良いです。
  • react-number-formatと組み合わせて <Number /> <Dec /> 等が作成可能
  • react-datetime と組み合わせて <Date />

勢いに乗って <Radio /> <Checkbox /> <Select />なんかも作成しました。 <Number /><Date />は癖があって稼働までに苦労したので機会&&反応があれば別途記述したいですね。

最後に

今回の記述方法は他のUI FrameworkをFormikに対応させる場合にも応用できると思います。

Why not register and get more from Qiita?
  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
No 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
ユーザーは見つかりませんでした