Tonny
Tonny

Reputation: 23

Problem with types when using shadcn FormField component

Type 'foo' is not assignable to type 'InputProps'

So, I'm trying to make a reusable component called TextInput that wraps the Shadcn FormField component, as the documentation shows its need to pass to Input component a "field" props that's come from the render method of the FormField. Here's the TextInput component:

import React from "react";
import {
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "./ui/form";
import { Input } from "./ui/input";
import { Control } from "react-hook-form";

type formSchema = {
  title: string;
  description: string;
  image: string;
  location: {
    number: number;
    country: string;
    state: string;
    district: string;
    street: string;
    complement: string;
  };
};

type control = Control<formSchema, any>;

type name = keyof formSchema;

type Props = {
  name: name;
  label: string;
  placeholder: string;
  description?: string;
  control: control;
};

const TextInput = ({
  name,
  label,
  placeholder,
  description,
  control,
}: Props) => {
  return (
    <FormField
      control={control}
      name={name}
      render={({ field }) => (
        <FormItem>
          <FormLabel>{label}</FormLabel>
          <FormControl>
            {/* Here's the problem */}
            <Input {...field} placeholder={placeholder} />
          </FormControl>
          <FormDescription>{description}</FormDescription>
          <FormMessage />
        </FormItem>
      )}
    />
  );
};

export default TextInput;

But when I pass the 'field' props I'm getting this error message from typescript:

Type '{ placeholder: string; onChange: (...event: any[]) => void; onBlur: Noop; disabled?: boolean | undefined; value: string | { number: number; country: string; state: string; district: string; street: string; complement: string; }; name: keyof formSchema; ref: RefCallBack; }' is not assignable to type 'InputProps'. Types of property 'value' are incompatible. Type 'string | { number: number; country: string; state: string; district: string; street: string; complement: string; }' is not assignable to type 'string | number | readonly string[] | undefined'. Type '{ number: number; country: string; state: string; district: string; street: string; complement: string; }' is not assignable to type 'string | number | readonly string[] | undefined'.

I tried to search both on shadcn and react-hook-form documentation and even on the Internet but I cannot find no clue on how to deal with it, I starded working recently with typescript so I never had a problem like this before. I also tried using the Controller input from react-hook-form but it's still getting this error. Here's the parent component for more context:

"use client";

import { Form, FormField } from "./ui/form";
import { SubmitHandler, useForm } from "react-hook-form";
import TextInput from "./TextInput";
import { Button } from "./ui/button";
import formSchema from "@/utils/meetSchema";
import * as z from "zod";
import { zodResolver } from "@hookform/resolvers/zod";

const MeetUpForm = () => {
  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
  });

  type Inputs = {};

  const onSubmit: SubmitHandler<Inputs> = (data) => console.log(data);

  return (
    <Form {...form}>
      <TextInput
        control={form.control}
        label="Title"
        placeholder="Type the meet up title"
        name="title"
      />
      <TextInput
        control={form.control}
        label="Description"
        placeholder="Type the meet up description"
        name="description"
      />
      <TextInput
        control={form.control}
        label="Image"
        placeholder="Type the meet up image URL"
        name="image"
      />
      <h1 className="text-xl font-bold my-4">Location</h1>
      <TextInput
        control={form.control}
        label="Country"
        placeholder="Type the meet up country"
        name="location"
      />
      <TextInput
        control={form.control}
        label="State"
        placeholder="Type the meet up state"
        name="location"
      />
      <TextInput
        control={form.control}
        label="District"
        placeholder="Type the meet up district"
        name="location"
      />
      <TextInput
        control={form.control}
        label="Street"
        placeholder="Type the meet up street"
        name="location"
      />
      <TextInput
        control={form.control}
        label="Number"
        placeholder="Type the meet up number"
        name="location"
      />
      <TextInput
        control={form.control}
        label="Complement"
        placeholder="Type the meet up location complement"
        name="location"
      />
      <Button type="submit">Submit</Button>
    </Form>
  );
};

export default MeetUpForm;

This is a personal and public project, so you can check the full code here: https://github.com/Tonny221/meet-up/tree/dev
Fell free to give me any feedback

Upvotes: 2

Views: 2396

Answers (1)

Shirshak kandel
Shirshak kandel

Reputation: 609

Yes I faced the same problem in my projects and fix it using UseFormReturn type from "react-hook-form". Below I make FormFieldComponent component.

For Video Explanation you can watch this video https://www.youtube.com/watch?v=E3YIY4Xxw88&ab_channel=shirshakKandel

{/* Country and phone */}
        <div className="grid lg:grid-cols-2 lg:gap-4">
          {/* Country */}
          <FormFieldComponent
            form={form}
            isPending={isPending}
            label="Country"
            name="country"
          />

          {/* Phone Number */}
          <FormFieldComponent
            form={form}
            isPending={isPending}
            label="Phone Number(Optional)"
            name="phone"
          />
        </div>

Definition of FormFieldComponent is like below. Here RegisterSchema is schema i made using zod.

const FormFieldComponent = ({
  form,
  isPending,
  label,  
  name,
  placeholder = "Type here",
}: {  
  form: UseFormReturn<z.infer<typeof RegisterSchema>>;
  isPending: boolean; 
  label: string;
  name: keyof z.infer<typeof RegisterSchema>;
  placeholder?: string;
}) => {
  return (
    <FormField
      control={form.control}
      name={name}
      render={({ field }) => (
        <FormItem>
          <FormLabel>{label}</FormLabel>
          <FormControl>
            <Input {...field} disabled={isPending} placeholder={placeholder} />
          </FormControl>
          <FormMessage />
        </FormItem>
      )}
    />
  );
};

Upvotes: 0

Related Questions