skip to Main Content

I would like to have a custom component for a password input with eye icon, as follows:

type FormValues = {
    email: string;
    fullName: string;
    password: string;
    confirmPassword: string;
};

type PasswordInputProps = {
    name: string;
    value: string;
    register: UseFormRegister<FormValues>;
    // onChange: () => void;
}

export default function PasswordInput({
    name,
    value,
    register,
    // onChange
}: PasswordInputProps) {
    const [passwordShown, setPasswordShown] = useState(false);

    const togglePassword = () => {
        setPasswordShown(!passwordShown);
    };

    return (
        <Styled.Container>
            <AuthStyled.Input
                type={passwordShown ? "text" : "password"}
                value={value}
                {...register(name as keyof FormValues)}
                name={name}
            // onChange={onChange}
            />
            <Styled.Eye onClick={togglePassword}>
                {!passwordShown ? <BsEye /> : <BsEyeSlash />}
            </Styled.Eye>
        </Styled.Container>
    );
}

I have managed to register it using react-hook-form v7, but cannot seem to find a way to add onChange behavior or to make it generic and not bound to FormValues (for example, that FormValues is for Registration, but would like to have the same field in the Login Page).

Its usage is like this:

<PasswordInput
    name="confirmPassword"
    value={confirmPassword}
    register={register}
    // onChange={(e) => setConfirmPassword(e.target.value)}
/>

Thanks in advance for any help you can provide.

2

Answers


  1. Chosen as BEST ANSWER

    The answer I found for handling both onChange and form agnostic was:

    type PasswordInputProps<T extends FieldValues> = {
        name: Path<T>;
        // value: string;
        register: UseFormRegister<T>;
        onChange: ChangeEventHandler<HTMLInputElement> | undefined;
    }
    
    export default function PasswordInput<T extends FieldValues>({
        name,
        register,
        onChange
    }: PasswordInputProps<T>) {
        const [passwordShown, setPasswordShown] = useState(false);
    
        const togglePassword = () => {
            setPasswordShown(!passwordShown);
        };
    
        return (
            <Styled.Container>
                <AuthStyled.Input
                    type={passwordShown ? "text" : "password"}
                    // value={value}
                    {...register(name)}
                    name={name}
                    onChange={onChange}
                />
                <Styled.Eye onClick={togglePassword}>
                    {!passwordShown ? <BsEye /> : <BsEyeSlash />}
                </Styled.Eye>
            </Styled.Container>
        );
    }
    

  2. Your onChange type should use value ChangeEventHandler<HTMLInputElement> | undefined

    type ComponentType = {
      onChange: ChangeEventHandler<HTMLInputElement> | undefined
    } 
    
    ...
    
    function Component({onChange}: ComponentType) {
    
    ...
    
      return (
    
        ...
    
        <MyTextInput 
          onChange={onChange}
        />
        
        ...
    
      )
    }
    

    EDIT: forgot to show usage:

    import MyComponent from '....'
    
    ....
    
    function handleOnChange(e: ChangeEvent<HTMLInputElement>) {
      console.log(e.target.value)
    }
    
    return (
      ...
    
      <MyComponent
        onChange={(e) => {console.log(e.target.value)}}
        # or
        onChange={handleOnChange}
      />
    )
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search