5
votes

I'm trying to manage a form using a custom hook, so i have this code

FormHook.tsx:

import { useState } from 'react';

interface ICampos  {
    name: string;
    email: string;
    password: string;
}


const useForm = (initialState: ICampos) => {
    
    const [values, setValues] = useState(initialState);


    const handleInputChange = ({ target }: any) => {
        setValues({
            ...values,
            [target.name]: target.value
        })
    };

    return [values, handleInputChange];
}

export default useForm

FormWithCustomHook.tsx:

import React from 'react'
import './effects.css' 
import useForm from '../../hooks/useForm';

interface ICampos  {
    name: string;
    email: string;
    password: string;
}

const FormWithCustomHook = () => {

    const [formValues, handleInputChange] = useForm({
        name: '',
        email: '',
        password: ''
    });

    const { name, email, password } = formValues;



    return (
        <>
            <h1> FormWithCustomHook </h1>
            <hr />

            <div className="form-group">
                <input
                    type="text"
                    name="name"
                    className="form-control"
                    placeholder="Tu nombre"
                    autoComplete="off"
                    value={name}
                    onChange={handleInputChange} />
            </div>
            
            <div className="form-group">

                <input
                    type="email"
                    name="email"
                    className="form-control"
                    placeholder="[email protected]"
                    autoComplete="off"
                    value={email}
                    onChange={handleInputChange} />
            </div>

            <div className="form-group">

                <input
                    type="password"
                    name="password"
                    className="form-control"
                    placeholder="*****"
                    autoComplete="off"
                    value={password}
                    onChange={handleInputChange} />
            </div>

        </>
    )
}

export default FormWithCustomHook;

And I'm stuck with this error on line of FormWithCustomHook.tsx:

const { name, email, password } = formValues;

It only marks the error only on email and password:

'Property 'email' does not exist on type 'ICampos | (({ target }: any) => void)'.ts(2339)'

And in my onChange inside of the inputs says:

Type 'ICampos | (({ target }: any) => void)' is not assignable to type '((event: ChangeEvent) => void) | undefined'. Type 'ICampos' is not assignable to type '(event: ChangeEvent) => void'. Type 'ICampos' provides no match for the signature '(event: ChangeEvent): void'.ts(2322) index.d.ts(2092, 9): The expected type comes from property 'onChange' which is declared here on type 'DetailedHTMLProps<InputHTMLAttributes, HTMLInputElement>''

I'd tryed to add types on the customhook.tsx but I really don't understand this error

1
I've updated my answer, showing one solution typing the array.Federico Alecci

1 Answers

3
votes

The hook doesn't know the order of the array. So it might be ICampos | Handler or Handler | ICampos. You have two options here:

You can type the return array on the hook:

const useForm = (
  initialState: ICampos
): [ICampos, ({ target }: any) => void] => {
  const [values, setValues] = useState<ICampos>(initialState);

  const handleInputChange = ({ target }: any) => {
    setValues({
      ...values,
      [target.name]: target.value
    });
  };

  return [values, handleInputChange];
};

Or you could return an object instead of an array. I prefer this one since I don't like typing arrays.

import { useState } from "react";

interface ICampos {
  name: string;
  email: string;
  password: string;
}

const useForm = (initialState: ICampos) => {
  const [values, setValues] = useState<ICampos>(initialState);

  const handleInputChange = ({ target }: any) => {
    setValues({
      ...values,
      [target.name]: target.value
    });
  };

  return { values, handleInputChange };
};

export default useForm;
import React from "react";
import "./effects.css";
import useForm from "./useForm";

interface ICampos {
  name: string;
  email: string;
  password: string;
}

const FormWithCustomHook = () => {
  const { values, handleInputChange } = useForm({
    name: "",
    email: "",
    password: ""
  });

  const { name, email, password } = values;

  return (
    <>
      <h1> FormWithCustomHook </h1>
      <hr />

      <div className="form-group">
        <input
          type="text"
          name="name"
          className="form-control"
          placeholder="Tu nombre"
          autoComplete="off"
          value={name}
          onChange={handleInputChange}
        />
      </div>

      <div className="form-group">
        <input
          type="email"
          name="email"
          className="form-control"
          placeholder="[email protected]"
          autoComplete="off"
          value={email}
          onChange={handleInputChange}
        />
      </div>

      <div className="form-group">
        <input
          type="password"
          name="password"
          className="form-control"
          placeholder="*****"
          autoComplete="off"
          value={password}
          onChange={handleInputChange}
        />
      </div>
    </>
  );
};

export default FormWithCustomHook;