Saltar al contenido principal
Logo de JSCamp

Creando custom hook para formulario

Aprende a extraer la lógica de un formulario a un custom hook para mantener el código limpio y modular

midudev

Más clases y directos en YouTube

Suscribirse

Creando custom hook para formulario

Introducción del problema

Ahora mismo tenemos un componente principal que gestiona toda la lógica de búsqueda, y eso lo hace demasiado complejo. El componente tiene mezclado:

  • handleSubmit
  • handleTextChange
  • La lógica de búsqueda (search)
  • IDs como tecnología, localización, experiencia, etc.

Todo esto junto hace que el componente sea difícil de leer y mantener. Vamos a separar responsabilidades usando un custom hook.

Identificando la complejidad

Cuando un componente tiene demasiadas responsabilidades, es señal de que necesitamos refactorizar. En nuestro caso, la lógica del formulario de búsqueda debería vivir en su propio hook personalizado.

Creando useSearchForm

Vamos a crear un custom hook llamado useSearchForm que encapsule toda la lógica del formulario.

Los custom hooks, a diferencia de hooks como el router que vimos en el vídeo anterior, pueden recibir parámetros igual que funciones normales.

Parámetros del hook

Nuestro hook necesita recibir:

  • onSearch: función que se ejecuta al buscar
  • onTextFilter: función para filtrar por texto
  • IDs necesarias para los filtros:
    • IDTechnology
    • IDLocation
    • IDExperienceLevel
    • Cualquier otra ID que use el componente
function useSearchForm({ onSearch, onTextFilter, IDTechnology, IDLocation, IDExperienceLevel }) {  // Lógica del hook aquí}

Añadiendo estado interno

El texto de búsqueda necesita estar en un estado. Lo añadimos dentro del custom hook:

function useSearchForm({ onSearch, onTextFilter, IDTechnology, IDLocation, IDExperienceLevel }) {  const [searchText, setSearchText] = useState('')  // Este estado:  // - guarda el texto introducido por el usuario  // - se actualiza en handleChange  // - será devuelto al componente para mostrarlo en el input}

Implementando los handlers

Ahora movemos la lógica de los handlers al hook:

function useSearchForm({ onSearch, onTextFilter, IDTechnology, IDLocation, IDExperienceLevel }) {  const [searchText, setSearchText] = useState('')  const handleSubmit = (event) => {    event.preventDefault()    const formData = new FormData(event.target)    const filters = {      text: formData.get('search'),      technology: formData.get('technology'),      location: formData.get('location'),      experience: formData.get('experience'),    }    onSearch(filters)  }  const handleChange = (event) => {    const text = event.target.value    setSearchText(text)    onTextFilter(text)  }  // Devolvemos lo que el componente necesita  return {    searchText,    handleSubmit,    handleChange,  }}

Usando el hook en el componente

Ahora nuestro componente se simplifica enormemente:

function SearchComponent() {  const { searchText, handleSubmit, handleChange } = useSearchForm({    onSearch: performSearch,    onTextFilter: filterByText,    IDTechnology: 'tech',    IDLocation: 'loc',    IDExperienceLevel: 'exp',  })  return (    <form onSubmit={handleSubmit}>      <input value={searchText} onChange={handleChange} name="search" />      {/* Resto del formulario */}    </form>  )}

El componente ya no implementa la lógica, solo consume las funciones y valores del hook.

Ventajas de esta refactorización

Código más limpio: el componente se enfoca en renderizar

Lógica reutilizable: el hook puede usarse en otros componentes

Más fácil de testear: podemos probar el hook independientemente

Mejor organización: separación clara de responsabilidades

Reglas importantes sobre custom hooks

1. Llamar solo en el nivel superior

Los custom hooks deben llamarse solo en el nivel superior del componente o de otro custom hook.

NO dentro de:

  • Callbacks (handleClick, handleSubmit, etc.)
  • Condicionales (if)
  • Loops (for, while)
  • useEffect ni otros hooks
// ❌ MAL - dentro de un callbackfunction Component() {  const handleClick = () => {    const { data } = useSearchForm() // ERROR  }}// ❌ MAL - dentro de un condicionalfunction Component() {  if (condition) {    const { data } = useSearchForm() // ERROR  }}// ✅ BIEN - nivel superiorfunction Component() {  const { searchText, handleSubmit } = useSearchForm()  const handleClick = () => {    // Usar los valores del hook aquí está bien    console.log(searchText)  }}

2. Deben empezar con el prefijo “use”

Como useState, useEffect, etc.

Esto no es solo una convención: React confía en ese nombre para validar reglas internas.

// ✅ BIENfunction useSearchForm() {}function useUserData() {}function useAuthentication() {}// ❌ MALfunction searchFormHook() {} // No empieza con "use"function getSearchForm() {} // No es un hook

3. Solo se pueden usar en componentes o custom hooks

Los custom hooks solo se pueden usar en:

  • Componentes de React
  • Otros custom hooks

NO en funciones normales fuera del ecosistema de React:

// ❌ MAL - función normalfunction calculateTotal() {  const { data } = useData() // ERROR  return data.total}// ✅ BIEN - componentefunction Component() {  const { data } = useData()  return <div>{data.total}</div>}// ✅ BIEN - otro custom hookfunction useCalculateTotal() {  const { data } = useData()  return data.total}

Verificando que todo funciona

Después de la refactorización, todo debe seguir funcionando exactamente igual que antes. La diferencia está en la organización del código, no en el comportamiento.

Conclusión

Extraer la lógica a custom hooks es una práctica esencial en React moderno:

  • El componente se enfoca en renderizar
  • La lógica vive en un sitio reutilizable
  • El desarrollo se vuelve más escalable

Esta técnica mantiene tu código:

  • ✅ Limpio
  • ✅ Modular
  • ✅ Fácil de testear
  • ✅ Mantenible a largo plazo

Usar custom hooks correctamente es señal de código React profesional y bien arquitecturado.