Home > Blockchain >  How to add more props into useController in React Hook Form with TypeScript?
How to add more props into useController in React Hook Form with TypeScript?

Time:11-08

I found that when i'm using useController hook for handling my form, my inputs doesn't have 'type' property. I think that is bad for semantic rules. I would like extend existing interface for properties like: type, label. Unfortunately i stuck, because of typescript.

Here is what i've tried so far.

import { useController, UseControllerProps } from 'react-hook-form';

export type FormValues = {
  title: string;
  amount: number;
  url: string;
  price: number;
};

const CustomInputHooked = (props: UseControllerProps<FormValues>) => {
  const { field, fieldState } = useController(props);
  console.log('props', props);
  console.log('field', field);
  console.log('fieldState', fieldState);

  return (
    <div>
      <input {...field} />
      <p>{fieldState.isTouched && 'Touched'}</p>
      <p>{fieldState.isDirty && 'Dirty'}</p>
      <p>{fieldState.invalid ? 'invalid' : 'valid'}</p>
    </div>
  );
};


import CustomInputHooked, { FormValues } from './custom-input/custom-input-hooked';

interface initialStateModel {
  title: string;
  amount: number;
  url: string;
  price: number;
}

const initialState: initialStateModel = {
  title: '',
  amount: 1,
  url: '',
  price: 0,
};

const AddItemHooked = () => {
  const { control, handleSubmit } = useForm<FormValues>({
    defaultValues: initialState,
    mode: 'onChange',
  });
  const onSubmit = (data: FormValues) => {
    console.log('SUBMIT', data);
  };
  return (
    <div className="d-flex vh-100 justify-content-center align-items-center">
      <form className={styles.form} onSubmit={handleSubmit(onSubmit)}>
        <CustomInputHooked control={control} name="title" rules={{ required: true }} />
        <CustomInputHooked
          control={control}
          name="amount"
          rules={{ required: true, maxLength: 5, pattern: /[0-9]/g }}
        />
        <CustomInputHooked control={control} name="url" rules={{ required: true }} />
        <CustomInputHooked control={control} name="price" rules={{ required: true }} />
        <input type="submit" />
      </form>
    </div>
  );
};

@UPDATE

I managed to do that. It is working but i'm pretty sure that my solution isn't elegant. Could you give me feedback?

I copied a lot of ts code and have added 2 more values..

import {
  Control,
  FieldPath,
  FieldPathValue,
  FieldValues,
  RegisterOptions,
  UnpackNestedValue,
  useController,
} from 'react-hook-form';

export type FormValues = {
  title: string;
  amount: number;
  url: string;
  price: number;
};

declare type FromProps<
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
> = {
  name: TName;
  rules?: Omit<
    RegisterOptions<TFieldValues, TName>,
    'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'
  >;
  shouldUnregister?: boolean;
  defaultValue?: UnpackNestedValue<FieldPathValue<TFieldValues, TName>>;
  control?: Control<TFieldValues>;
  type?: 'text' | 'number' | 'date';
};

const CustomInputHooked = (props: FromProps<FormValues>) => {
  const { field, fieldState } = useController(props);
  console.log('props', props);
  console.log('field', field);
  console.log('fieldState', fieldState);

  return (
    <div>
      <input {...field} type={props.type} />
    </div>
  );
};

CodePudding user response:

If I understand correctly, you want your CustomInputHooked component to accept the same props as useController, plus an extra type prop so that you can properly specify your child native <input> element?

In that case, you can simply use an intersection. And you can use a rest operator to separate the type prop, so that you do not pass it to useController.

const CustomInputHooked = ({
  type,
  ...props
}: {
  type?: 'text' | 'number' | 'date'
} & UseControllerProps<FormValues>) => {
  const { field, fieldState } = useController(props);
  
  return (
    <div>
      <input {...field} type={type} />
    </div>
  );
};
  • Related