Formikでreact-bootstrapを使いたい

#はじめに
先日`react-redux-from`から`Formik`に切り換えたのですが、普段使いのUI Frameworkは[react-bootstrap](https://react-bootstrap.netlify.com/)なんです。`Formik`の公式を見ると、[未対応じゃないですか](https://jaredpalmer.com/formik/docs/3rd-party-bindings)。[reactstrap](https://reactstrap.github.io/)っていつの間にか`Bootstrap4`に対応していたんですね…。知らなかった…。ドキュメントとか不親切だから`react-bootstrap`の方が良いと思うんですけどね。

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

- [Input Group](https://getbootstrap.com/docs/4.4/components/input-group/)に対応
- `<Field name="name" component={Text} />`よりも `<Text name="name" />`のような使い方がしたい。
- `onChange` `onBlur`をハンドリング

#とりあえず作ってみる

```jsx: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>
);

```

上記、一件良さそうに見えます。しかも[公式が推奨している書き方](https://getbootstrap.com/docs/4.4/components/forms/#custom-styles)だし。でも、`InputGroup`を使うとコンポーネントの角が出てイケてないんです。こんな風になる。
<img width="390" alt="スクリーンショット 2020-03-08 11.35.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201998/54dcd847-665b-bea7-d6fa-ad0a06e51c6e.png">

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

```jsx
<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`を上書きします。これにより理想の出力となります。公式も修正した方が良いと思うんですけどね。

使うときはこんな感じ

```jsx
<Text name="zipcode" type="tel" prependText="〒" />
```

以下のように表示されます。上手くいきました。
<img width="392" alt="スクリーンショット 2020-03-08 12.25.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201998/e07f7bb3-24fe-4359-3995-554960adce4e.png">


#onChange onBlurに対応させる
`<Field />`はデフォルトで　onChange onBlurをフックしちゃうので一手間かけます。

```jsx
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>
);
```

これにより

```jsx

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](https://github.com/s-yadav/react-number-format)と組み合わせて `<Number />` `<Dec />` 等が作成可能
- [react-datetime](https://github.com/YouCanBookMe/react-datetime) と組み合わせて `<Date />`

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

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