Caio Gomes
Caio Gomes

Reputation: 768

Improper return type with custom hook from useState

I have the following generic custom hook made with typescript:

import { useState } from "react"

export const useForm = <T>(initialValues: T) => {
    const[values, setValues] = useState<T>(initialValues)
    return [
        values,
        (        
        e: { target: HTMLInputElement}) => {
            setValues({
                ...values,
                [e.target.name]: e.target.value
            })
        }
    ]
}

Then I'm trying to use it:

import React from 'react';
import { useForm } from './useForm';

type FormValues = {
  email: string,
  password: string
}

const App : React.FC = () => {
  const initialValues: FormValues = {email: "", password: ""}
  const [values, handleChanges] = useForm<FormValues>(initialValues);

  return (
    <div>
      <input type="text" name="email" value={(values.email} onChange={e => handleChanges}/>
      <input 
        type="password"
        name="password"
        value={values.password}
        onChange={e => handleChanges}/>
    </div>
  )
}

My problem is around

values.email

and

values.password

The error message is:

Property 'email' does not exist on type 'FormValues | ((e: { target: HTMLInputElement; }) => void)'. Property 'email' does not exist on type '(e: { target: HTMLInputElement; })

Property 'password' does not exist on type 'FormValues | ((e: { target: HTMLInputElement; }) => void)'. Property 'password' does not exist on type '(e: { target: HTMLInputElement; })

If values is an unioun between FormValues and another value shouldn't I be able to access email and password from it?

Upvotes: 0

Views: 251

Answers (1)

Caio Gomes
Caio Gomes

Reputation: 768

The problem is my return type. It seems I have to explicitly put it on the code signature.

import { useState } from "react"
import { ChangeEvent } from "react"

function useForm<T>(
    initialState: T,
  ): [T, (e: ChangeEvent<HTMLInputElement>) => void] {
    const [values, setValues] = useState<T>(initialState);

    function handleChange(e: ChangeEvent<HTMLInputElement>): void {
      setValues({
        ...values,
        [e.target.name]: e.target.value,
      });
    }

    return [values, handleChange];
  }

  export default useForm;

Upvotes: 2

Related Questions