React Final Form #01 かんたんにフォームを構築する

# この記事を読むと幸せになれそうな人
`React.useState` や Redux を使う方法で大きめのフォームを構築するとき、UIライブラリを使ったり、バリデーションを実装したりすると、ボイラープレートコードが多すぎて辟易してしまうと思います。
そんな人には、この [**React Final Form**](https://final-form.org/react) がオススメかもしれません。

(個人的には React Hooks Form よりもこっちの方が明示的で好きです...)

# 今回やること
- **#01『かんたんにフォームを構築する』←今ここ**
- [#02『Render Propsを使って、かんたんにバリデーションを実装する』](https://qiita.com/honey32/items/0a7872d8733656effea3)
- [#03『かんたんに２回クリックの防止とサーバー側バリデーションを実装する』](https://qiita.com/honey32/items/3ed19b950d7aeecbd4cf)

今回はとりあえず、フォームを構築して、送信処理を実装するところまでを実装します。

# 1. `Form`コンポーネントを設置する。

:::note
当記事では、テスト用のページを手早く用意するために Next.js を、使い慣れているので Typescript を使っていますが、内容には特に関係ありません。
テストする場合はそれぞれのやりやすい環境でどうぞ。
:::

まずは、中身も空っぽ、送信時に何もしないフォームを作成してみましょう。

```tsx:pages/react-final-forms/index.tsx
const ReactFinalFormPage: NextPage = () => {
  const handleSubmit: FormProps["onSubmit"] = (s) => {
    /* 処理の中身からっぽ */
  };
  return (
    <Form
      onSubmit={handleSubmit}
      render={({ handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <div>からっぽのフォームです</div>
        </form>
      )}
    />
  );
};
export default ReactFinalFormPage;
```

`<Form>`コンポーネント は、必須として、`onSubmit` prop を受け取ります。
( asyncな関数も渡すことが出来ます。)

その次には、 <code>render: (props: [FormRenderProps](https://final-form.org/docs/react-final-form/types/FormRenderProps)) => ... </code>  prop にレンダー関数を渡しています。

FormRenderProps から分割代入で取り出した `handleSubmit` 関数を、内側の `<form/>`要素の `onSubmit` prop に渡すことになっています。

(言わずもがな、 const宣言されている `handleSubmit` とは別です。)





# 2.送信ボタンを追加する

それでは、とにかくダミーの送信処理と、送信ボタンを追加してみましょう。

```diff_tsx:pages/react-final-forms/index.tsx
+ const waitMs = (ms: number) =>
+    new Promise((resolve) => setTimeout(resolve, ms));
  
  const ReactFinalFormPage: NextPage = () => {
    const handleSubmit: FormProps["onSubmit"] = async (s) => {
+     await waitMs(1000);
+     window.alert("送信されました！");
    };
    return (
      <Form
        onSubmit={handleSubmit}
        render={({ handleSubmit }) => (
          <form onSubmit={handleSubmit}>
-            <div>からっぽのフォームです</div>
+            <button type="submit">送信</button>
          </form>
        )}
      />
    );
  };
  export default ReactFinalFormPage;
```

`handleSubmit`関数では、仮の処理として、1秒待ってからアラートを表示する、という処理を書いています。

これを呼び出す **"送信" ボタン** を追加するには、`<form>`要素の内側に `<button type="submit">` 要素を放り込むだけでOKです。

# 3. `Form`の中に`Field`を設置する

その次に、フィールドを１つ追加してみましょう。

```diff_tsx:pages/react-final-forms/index.tsx
  const waitMs = (ms: number) =>
    new Promise((resolve) => setTimeout(resolve, ms));

+ const initialValues = {
+  name: "aa",
+ };

+ type ValuesType = typeof initialValues;

  const ReactFinalFormPage: NextPage = () => {
-   const handleSubmit: FormProps["onSubmit"] = async (s) => {
+   type HandleSubmit = FormProps<ValuesType, ValuesType>["onSubmit"];
+   const handleSubmit: HandleSubmit = async (s) => {
      await waitMs(1000);
-     window.alert("送信されました！");
+     window.alert(`JSON: ${JSON.stringify(s)}`);
      // 例) JSON: {"name":"山田太郎"}
    };
    return (
      <Form
        onSubmit={handleSubmit}
+       initialValues={initialValues}
        render={({ handleSubmit }) => (
          <form onSubmit={handleSubmit}>
+           <Field name="name" component="input" />
            <button>送信</button>
          </form>
        )}
      />
    );
  };
  export default ReactFinalFormPage;
```

**"name"** という名前を持つフィールドを追加しています。

`<Field>`コンポーネントに `component` Prop に `"input"`と渡すと、 標準の`<input>`要素を表示してくれます。


`<Form>`コンポーネントには、 フォームの初期状態のオブジェクトを `initialValue` Prop に渡します。

送信処理では、フォームの状態を参照して、その内容をJSON形式で出力するようにしています。

:::note warn
(Typescript限定)
`initialValues`の型(`ValuesType`)を`FormProps`型の型引数に渡さないと、型のエラーになります。
また、それによって、送信処理の引数 `s` の型が、`ValuesType` 型に推論されます。
:::

# まとめ

いかがだったでしょうか？
React Final Form を使えば、Controlled Component でフォームを作るときのしんどさがかなり緩和されたことが、最小のフォームを作っただけでもわかると思います。
次回以降では、フォームのバリデーションにも触れてみたいと思います。

https://qiita.com/honey32/items/0a7872d8733656effea3


# あとがき：このライブラリの特徴

- 追加される２つのコンポーネント `<Form/>`, `<Field/>` に、 [**Render Prop**](https://ja.reactjs.org/docs/render-props.html) を渡す形式
  - それぞれに、 `<form/>` 要素、 MUI (Material UI) の入力のコンポーネントなどを描画するための **レンダー関数**を渡すことができる
  - そのレンダー関数の引数を通じて、多様な切り口から見たフォーム・フィールドの状態・フラグにアクセスできる。
    - ( ↓ の緑枠に一部を列挙した)
  - `subscribe={{...}}` prop に、「レンダー関数の引数のうち、このプロパティだけ監視する」と明示すると、 **不要な再描画を防いでくれる**。
    - subscription-based な設計らしい

:::note

フィールドのバリデーションメッセージの表示切り替えに使えそうなフラグの一部

- フィールドの状態が、初期状態(`initialState`)に対して
  - **dirty**: 差分がある時に `true`
  - **pristine**: その否定 `pristine === !dirty`
- **modified**: フィールドが一度でも変更されたことがあるなら `true`
- **visited** : フィールドが一度でもフォーカスされたことがあるなら `true`
- **touched**: フィールドが一度でも「フォーカス→フォーカスを外す」をされたことがあるなら `true`

:::


# See also:

Final Form と、そのReactバインディングである React Final Form のドキュメントです

https://final-form.org/docs/react-final-form/getting-started

この動画でのライブコーディングも、大いに参考になると思います。

https://youtu.be/WoSzy-4mviQ