skip to Main Content

so um I’ve been trying to make a reusable Input component so I don’t have to copy paste all of the styles everywhere and I made this
import { FieldValues, UseFormRegister, Path } from ‘react-hook-form’;

interface Props<T extends FieldValues> {
  label: string;
  id: keyof T;
  type?: string;
  register: UseFormRegister<T>;
  error?: string;
}

const FormField = <T extends FieldValues>({
  label,
  id,
  type = 'text',
  register,
  error,
}: Props<T>) => {
  return (
    <div className='flex flex-col gap-4 border p-2'>
      <label className='font-bold' htmlFor={id as string}>
        {label}
      </label>
      <input type={type} id={id as string} {...register(id as Path<T>)} />
      {error && <span className='text-red-500'>{error}</span>}
    </div>
  );
};

export default FormField;

But I feel like this is not the right way to do it or maybe it is at this point I don’t know. if anyone has built a similar component I would appreciate help and suggestion

2

Answers


  1. you should try adding more flexibility by accepting additional props that you can spread onto the input element, such as className, placeholder, disabled, etc., to customize the input further and try adding additional styling or accessibility features to make errors more noticeable to users, such as changing the input border color or adding an aria role.

    you can also add comments to explain each prop and its purpose and examples of how to use the component.

    import { FieldValues, UseFormRegister, Path } from 'react-hook-form';
    
    // define props interface for the FormField component
    interface Props<T extends FieldValues> {
      label: string;                // label for the input field
      id: keyof T;                  // unique identifier for the input field
      type?: string;                // type of the input field (default is 'text')
      register: UseFormRegister<T>; // function provided by react-hook-form for registering the input field
      error?: string;               // error message to display (optional)
      className?: string;           // additional css class name for styling (optional)
      placeholder?: string;         // placeholder text for the input field (optional)
      disabled?: boolean;           // flag to disable the input field (optional)
    }
    
    // define the FormField component
    
    const FormField = <T extends FieldValues>({
      label,
      id,
      type = 'text',
      register,
      error,
      className,
      placeholder,
      disabled,
    }: Props<T>) => {
      
      return (
        <div className={`flex flex-col gap-4 border p-2 ${className}`}>
          {/* Label for the input field */}
          <label className='font-bold' htmlFor={id as string}>
            {label}
          </label>
          {/* Input field */}
          <input
            type={type} // Input type
            id={id as string} // Unique identifier
            {...register(id as Path<T>)} // Register the input field with react-hook-form
            placeholder={placeholder} // Placeholder text (if provided)
            disabled={disabled} // Disable the input field (if specified)
            className={`border rounded p-2 ${error ? 'border-red-500' : ''}`} // CSS classes for styling
          />
          {/* Display error message (if provided) */}
          {error && <span className='text-red-500'>{error}</span>}
        </div>
      );
    };
    
    export default FormField;
    
    Login or Signup to reply.
  2. Since you already know you’ll be using react-hook-form to implement your reusable input, you can make it an internal dependency with useFormContext; this way there’s less data to pass with props.

    You can also add type-safety to your Props.type field.

    Updated code below:

    import { HTMLInputTypeAttribute as InputType } from "react";
    import { FieldValues, Path, get, useFormContext } from "react-hook-form";
    
    interface Props<T extends FieldValues, K extends InputType> {
      label: string;
      type?: K;
      id: Path<T>;
    }
    
    const FormField = <T extends FieldValues, K extends InputType = InputType>({
      label,
      id,
      type = "text" as K,
    }: Props<T, K>) => {
      const {
        register,
        formState: { errors },
      } = useFormContext<T>();
    
      const error = get(errors, id)?.message;
    
      return (
        <div className="flex flex-col gap-4 border p-2">
          <label className="font-bold" htmlFor={id}>
            {label}
          </label>
          <input type={type} id={id} {...register(id)} />
          {error && <span className="text-red-500">{error}</span>}
        </div>
      );
    };
    
    export default FormField;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search