React Hooksでformもuseして型安全なフォーム生活


はい。

とくちょう

簡単な例です。

import * as React from 'react';
import {FC} from 'react';

import {useForm, createFormScope} from 'mayoiga';
import {Input, NumberInput} from 'mayoiga/lib/forms';

const required = (target: string) => (target.length === 0 ? 'required' : undefined);

const between = (min: number, max: number) => (target: number) => {
  if (target < min) {
    return `more than ${min}`;
  }
  if (target > max) {
    return `less than ${max}`;
  }
};

const choice = function<T>(...candidates: Array<T>) {
  return (target: T) => (candidates.includes(target) ? undefined : 'You should choose from the candidates.');
};

const INITIAL_FORM_STATE = {
  name: '',
  age: 13,
  seed: 'fish',
};

const {context, scope} = createFormScope<typeof INITIAL_FORM_STATE>();

const DemoForm = scope(props => {
  const {Form, Field} = useForm(context);
  return (
    <Form onSubmit={value => console.log(value)}>
      <Field name="name" component={Input} validations={[required]} />
      <Field name="age" component={NumberInput} validations={[between(5, 20)]} />
      <Field name="seed" component={Input} validations={[choice('fish', 'squid', 'octopus')]} />

      <button disabled={!props.touched || Object.values(props.errors).some(e => !!e.length)}>submit</button>
    </Form>
  );
});

export default function DemoApp() {
  return <DemoForm initialState={INITIAL_FORM_STATE} onSubmit={value => alert(`submit ${JSON.stringify(value)}`)} />;
}

一番重要なのは

const {context, scope} = createFormScope<typeof INITIAL_FORM_STATE>();

const DemoForm = scope(props => {
  const {Form, Field} = useForm(context);
  return (
    <Form onSubmit={value => console.log(value)}>
      <Field name="name" component={Input} validations={[required]} />
      <Field name="age" component={NumberInput} validations={[between(5, 20)]} />
      <Field name="seed" component={Input} validations={[choice('fish', 'squid', 'octopus')]} />

      <button disabled={!props.touched || Object.values(props.errors).some(e => !!e.length)}>submit</button>
    </Form>
  );
});

export default function DemoApp() {
  return <DemoForm initialState={INITIAL_FORM_STATE} onSubmit={value => alert(`submit ${JSON.stringify(value)}`)} />;
}

のくだりですね。
useForm できます。え、すごーい! キャッシュも結構きかせてるからパフォーマンスも悪くないっぽいよ!(たぶん)

そしてこのライブラリは型を頑張ってるので <Field /> に渡すnameが createFormScope で渡した型引数のkeyに存在しない値である場合はTypeScriptがエラーを出してくれます。これでageと間違えてsageても問題ありませんね。
さらにcomponent=で渡しているコンポーネントの valueonChangeState[Name](たとえばageであればINITIAL_FORM_STATE['age']の型と互換であるかどうか。つまりこの場合はnumberである必要がある)もチェックしてくれるんだって!

ちなみに<Form />レベルのonSubmitはoptionalです。

特徴を簡単にまとめると

  • 型がかなり効く
  • useFormできるので最新版を使っているという気持ちになれる。ボイラープレートコードが少ない
  • ちょっとしたグルーを書けばantdやbootstrapやmaterialやらwhateverと簡単に連携できる

です。

こちら にもうちょっと詳しい説明があります。


最後にこの動画が面白い、役に立つと思った方は高評価とチャンネル登録、リポジトリにスターをお願いします。またね!