💬

【Typescript】【React-Hook-Form】型安全なフォーム用の入力コンポーネントの作成方法

2023/01/13に公開

概要

React-Hook-Formを使用して、フォームの入力用の汎用的なコンポーネントを作成したい場面があると思います。

その際にTypescriptで型推論が効く形でのコンポーネントの作成方法です。

ソース


type TFormValues = {
    hoge: number,
    huga: string,
}

const defaultValues = {
    hoge: 1,
    huga: 'huga',
}

export const ParentComponent = function _ParentComponent() {
    const form = useForm({
        defaultValues,
    });

    return (
        <FormProvider {...form}>
            <ChildComponent<TFormValues> target='' />
        </FormProvider>
    );
};

type TChildComponentProps<T extends FieldValues> = {
    target: Path<T>;
    disabled?: boolean;
    type?: HTMLInputTypeAttribute;
};

export const ChildComponent = <T extends FieldValues>({
    target,
    disabled = false,
    type = 'text',
}: TChildComponentProps<T>) => {
    const { register } = useFormContext<T>();
    return <input disabled={disabled} type={type} {...register(target)} />;
};

解説

React-Hook-formでは以下の型を提供してくれます

  • FieldValues
  • Path<T>

useFormフック内のregisterを使用する場合は、React-Hook-Form関数の組み込みの機能になるので、
型推論が働くのですが、自作関数だと型が決まらず型安全なコンポーネントとはならないため、上記の二つの型を使い型が決まるように変更していきます。

targetの値をフォームに入るキーに限定する

type TChildComponentProps<T extends FieldValues> = {
    target: Path<T>;
    disabled?: boolean;
    type?: HTMLInputTypeAttribute;
};

export const ChildComponent = <T extends FieldValues>({
    target,
    disabled = false,
    type = 'text',
}: TChildComponentProps<T>) => {
    const { register } = useFormContext<T>();
    return <input disabled={disabled} type={type} {...register(target)} />;
};

ChildComponentのプロパティのtargetをuseForm内で保持するプロパティのみに絞るため、Path<T>とします。

Path<T>は、挿入したオブジェクトからキー値の共有体型を生成します。

型引数にReact-Hook-Fromで使用する型を挿入する

type TFormValues = {
    hoge: number,
    huga: string,
}

export const ParentComponent = function _ParentComponent() {
    const form = useForm({
        defaultValues,
    });

    return (
        <FormProvider {...form}>
            <ChildComponent<TFormValues> target='huga' />
        </FormProvider>
    );
};

ChildComponentの型引数に対し、React-Hook-Fromで使用する型引数を追加してあげれば完了です。

最後に

今回は限定的な使用方法ですが、ジェネリクスの機能を使用すれば汎用的なコンポーネントをより型安全に使用することができそうです。

Discussion