khierl
khierl

Reputation: 635

How to properly use useField hook from Formik in typescript

I followed everything from documentation and watched the tutorial by Ben Awad on YouTube. And still, I cannot get it to work.

const TextField = (props: FieldHookConfig<{}>) => {
    const [field] = useField(props);
        return (
            <div>
                <input {...field} {...props}/>
            </div>
        );
    };

I used the FieldHookConfig as the type for the props because the useField is expecting a string or the FieldHookConfig base on the Field.d.ts file. yet typescript is still not happy.

it complains right in this line <input {...field} {...props}/>

(property) JSX.IntrinsicElements.input: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>
Type '{ ref?: string | ((instance: HTMLInputElement | null) => void) | RefObject<HTMLInputElement> | null | undefined; key?: string | number | undefined; ... 289 more ...; innerRef?: ((instance: any) => void) | undefined; } | { ...; } | { ...; }' is not assignable to type 'DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>'.
  Type '{ ref?: string | ((instance: HTMLSelectElement | null) => void) | RefObject<HTMLSelectElement> | null | undefined; key?: string | number | undefined; ... 269 more ...; checked?: boolean | undefined; }' is not assignable to type 'DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>'.
    Type '{ ref?: string | ((instance: HTMLSelectElement | null) => void) | RefObject<HTMLSelectElement> | null | undefined; key?: string | number | undefined; ... 269 more ...; checked?: boolean | undefined; }' is not assignable to type 'ClassAttributes<HTMLInputElement>'.
      Types of property 'ref' are incompatible.
        Type 'string | ((instance: HTMLSelectElement | null) => void) | RefObject<HTMLSelectElement> | null | undefined' is not assignable to type 'string | ((instance: HTMLInputElement | null) => void) | RefObject<HTMLInputElement> | null | undefined'.
          Type '(instance: HTMLSelectElement | null) => void' is not assignable to type 'string | ((instance: HTMLInputElement | null) => void) | RefObject<HTMLInputElement> | null | undefined'.
            Type '(instance: HTMLSelectElement | null) => void' is not assignable to type '(instance: HTMLInputElement | null) => void'.
              Types of parameters 'instance' and 'instance' are incompatible.
                Type 'HTMLInputElement | null' is not assignable to type 'HTMLSelectElement | null'.
                  Type 'HTMLInputElement' is missing the following properties from type 'HTMLSelectElement': length, options, selectedIndex, selectedOptions, and 4 more.ts(2322)

Upvotes: 17

Views: 17061

Answers (5)

Satinos
Satinos

Reputation: 179

@Pavlo

No need to redefine 'FieldHookConfig' again and add your custom fields.

Simply, you can extend 'FieldHookConfig' using the & operator like this:

type ExtendedFieldHook = FieldHookConfig<string> & { label: string }

And then, the component will look like:

export const TextInput = ({ label, ...props }: ExtendedFieldHook) => {
  const [field, meta]  = useField(props);
  return (
    <div className=" my-3">
      {meta.touched && meta.error ? (
        <>
          <input className="form-control" {...field} {...props} />
          <div className="error error-check text-danger">{meta.error}</div>
        </>
      ) : (
        <input className="form-control" {...field} {...props} />
      )}
    </div>
  );
};

Upvotes: 0

Pavlo Omelianchuk
Pavlo Omelianchuk

Reputation: 21

This is how I solved this issue, using formik docs

Actually Typescript was satisfied just after I added 'name: string;' to my InputProps.

type InputProps = {
  label: string;
  name: string;
  validate?: (value: any) => undefined | string | Promise<any>;
  type?: string;
  multiple?: boolean;
  value?: string;
};

export const TextInput = ({ label, ...props }: InputProps) => {
  const [field, meta]  = useField(props);
  return (
    <div className=" my-3">
      {meta.touched && meta.error ? (
        <>
          <input className="form-control" {...field} {...props} />
          <div className="error error-check text-danger">{meta.error}</div>
        </>
      ) : (
        <input className="form-control" {...field} {...props} />
      )}
    </div>
  );
};

Upvotes: 2

Otman Bouchari
Otman Bouchari

Reputation: 336

For anyone still experiencing the same issue try the following:

import { ClassAttributes, InputHTMLAttributes } from "react"
import { useField, FieldHookConfig } from "formik"

const TextField = (props: InputHTMLAttributes<HTMLInputElement> & ClassAttributes<HTMLInputElement> & FieldHookConfig<string>) => {
    const [field] = useField(props);
        return (
            <div>
                <input {...field} {...props}/>
            </div>
        );
    };

And if you have a destructured props you could do this:

type TextFieldProps = {
  label: string;
};

const TextField = ({label, ...props}: TextFieldProps & InputHTMLAttributes<HTMLInputElement> &
ClassAttributes<HTMLInputElement> &
FieldHookConfig<string>) => {
    const [field] = useField(props);
        return (
            <div>
                <input {...field} {...props}/>
            </div>
        );
    };

Upvotes: 7

Paul Maurer
Paul Maurer

Reputation: 380

Just to add to the above, the useField() documentation referenced above shows label being included in the MyTextField properties. In order to make this type of call:

<TextField name="firstName" type="text" placeholder="Jane" label="First Name" />

add the following interface:

interface OtherProps {
  label : string
}

Then change the first line of the function expression above to:

const TextField = (props : OtherProps & FieldHookConfig<string>) => {

You can then reference the passed label by calling props.label within the function's return block like this, for example:

<label htmlFor={props.id || props.name}>{props.label}</label>

Upvotes: 12

flying_pig
flying_pig

Reputation: 186

There are 2 problems, firstly, you cannot spread the props variable on the <input> element, due to the incompatible types (as specified in the error). Secondly, your generic type for FieldHookConfig shouldn't be {}, rather it should be string

So to fix it, suppose you are using your <TextField> element like so

<TextField
  name="firstName"
  type="text"
  placeholder="Jane"
/>

Then inside your TextField definition, you will write

const TextField = (props: FieldHookConfig<string>) => {
  const [field] = useField(props);
  return (
    <div>
      {/* no need to pass the name field because Formik will accept
      that prop internally and pass it to the field variable */}
      <input {...field} placeholder={props.placeholder} type={props.type} />
    </div>
    );
};

Upvotes: 17

Related Questions