React + FrameWork UI の form validationについて

# 概要

- Ant Design
- material UI

の2つでform validationを実装する。

## まえがき

今年の初めにAnt Designでform validationを実装したところ、
少し詰まった & 理解できてない部分があったので、整理しようと着手しました。

するとアップデートされていたようで一瞬でできてしまったので、material UI でも試してみることに。

## 実装

### Ant Design

[公式](https://ant.design/components/form/)

[実際に動いてるものはこちら](https://codesandbox.io/s/antd-form-validation-y4ne4?file=/src/App.tsx:0-1978)

コード

```js
import { Form, Input, Button, Checkbox } from "antd";

const App = () => {
  const onFinish = (values: any) => {
    console.log("Success:", values);
  };

  const onFinishFailed = (errorInfo: any) => {
    console.log("Failed:", errorInfo);
  };

  const USERNAME = ["test", "same name"];

  return (
    <Form
      name="basic"
      labelCol={{ span: 8 }}
      wrapperCol={{ span: 16 }}
      initialValues={{ remember: true }}
      onFinish={onFinish}
      onFinishFailed={onFinishFailed}
      autoComplete="off"
    >
      <Form.Item
        label="Username(same nameと入力してfocusを外すとエラーが出ます。文字数制限は4~100)"
        name="username"
        validateTrigger="onBlur"
        rules={[
          {
            min: 4,
            max: 100,
            message: "Usernameは最低4文字最大100文字でお願いします"
          },
          {
            validator: async (_, value) => {
              USERNAME.map((name) => {
                if (name === value) {
                  throw new Error("この名前は既に使用されています");
                } else {
                  return Promise.resolve();
                }
              });
            }
          }
        ]}
      >
        <Input />
      </Form.Item>

      <Form.Item
        label="Password(入力ごとにvalidationが走ります。default設定)"
        name="password"
        rules={[
          {
            min: 4,
            max: 8,
            message: "Passwordは最低4文字最大8文字でお願いします"
          },
          {
            required: true,
            message: "Passwordは必須です"
          }
        ]}
      >
        <Input.Password />
      </Form.Item>

      <Form.Item
        name="remember"
        valuePropName="checked"
        wrapperCol={{ offset: 8, span: 16 }}
      >
        <Checkbox>Remember me</Checkbox>
      </Form.Item>

      <Form.Item wrapperCol={{ offset: 8, span: 16 }}>
        <Button type="primary" htmlType="submit">
          Submit
        </Button>
      </Form.Item>
    </Form>
  );
};

export default App;
```

Ant Designは公式がかなり読みやすくなってたので、ぜひ読むのお勧めします。めんどくさがらずに。

基本的にはrulesの中にvalidation ruleを入れていきます。
例えば `min: 4` だと最小4文字ってことですね。
カスタマイズしたい時は ` validator` の中で関数を書いてください。

validationのタイミングは初期値がonChange(入力毎)です。
変更したい場合は `validateTrigger` を変更します。
非常にわかりやすい構成でした。

### material UI

[公式](https://mui.com/components/text-fields/)を見ていると

```js
<TextField inputProps={{ inputMode: 'numeric', pattern: '[0-9]*' }} />
```

みたいな記述は見つけて…あれ…これだけ？
と思い [TextFieldAPI](https://mui.com/api/text-field/)で `valida`と検索かけるもヒットせず。

あれ…
と思って見返してたら、

>For more advanced use cases, you might be able to take advantage of:
react-hook-form: React hook for form validation.

(もっとカスタマイズしたかったら、react-hook-form使うといいやで)

まじですCAR

Ant Designが親切すぎただけかな。

### material UI + react hook form

[公式](https://react-hook-form.com/jp/get-started#IntegratingwithUIlibraries)

[実際に動いてるものはこちら](https://codesandbox.io/s/react-hook-form-material-ui-validation-umq5k?file=/src/App.tsx)

```js
import React from "react";
import { useForm, Controller, SubmitHandler } from "react-hook-form";
import Input from "@material-ui/core/Input";

interface IFormInput {
  firstName: string;
}

const App = () => {
  const {
    getValues,
    control,
    handleSubmit,
    formState: { errors }
  } = useForm<IFormInput>({ mode: "onBlur" });

  const onSubmit: SubmitHandler<IFormInput> = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>Username(same name と打つとエラーが出ます)</label>
      <br />
      <Controller
        name="firstName"
        control={control}
        defaultValue=""
        render={({ field }) => <Input {...field} />}
        rules={{
          validate: () => {
            return getValues("firstName") !== "same name";
          }
        }}
      />
      {errors.firstName && <div>その名前は既に利用されています。</div>}
      <p>
        <input type="submit" />
      </p>
    </form>
  );
};

export default App;
```

react hook formの公式初めて読んだんですがめちゃわかりやすい。日本語ですし、情報も整理されててよき。
これは `react hook form` の中に `material UI` を組み込んで使用した例です。
`render` の中の `Input` がmaterial UIの部分ですね。

こちらもAnt Designと同様に `rules` の `validate` の中にいろいろ書いていけます。
validationのタイミングは `useForm<IFormInput>({ mode: "onBlur" });` ここで変更できます。

その他の詳細は公式わかりやすすぎたので、そちらがいいかと。

#### ちなみに

react hook form は Ant Designにも対応してるようなので、Ant Designだけでどうにもならない場合はreact hook form への組み込みご検討ください。

## まとめ

react hook formのおかげで大体なんとかなりそう。
FrameWork UIのカスタマイズは面倒なイメージですが、綺麗にwrapしてくれてました。

(コードはちゃちゃっと作ったものでプロダクトで試しておらず、簡易な動作確認しかしていないのでその点はご了承ください)

## あとがき

Ant Designで最初に実装した時は
「formはasync validatorを使ってるから詳しくはそっちみて」とGitHubのURLがあるだけで、
欲しい情報も中国語の掲示板にあったりして苦労した記憶があったのですが、
久しぶりに見返すととてもよかった。

Ant DesignのUI個人的には好きなんですが、シェア率低くて記事がないのがつらい…
(QiitaでもAntDesignのtag23個しかなかったし)