DEV Community

Cover image for 🔹Guía definitiva de React Hooks (1/8)
Pedro Alvarado
Pedro Alvarado

Posted on • Edited on

🔹Guía definitiva de React Hooks (1/8)

Este guía está diseñada para llevarte desde los conceptos básicos hasta patrones avanzados, con ejemplos prácticos, consejos de buenas prácticas y advertencias sobre errores comunes.


📋 Tabla de contenidos

  1. useState – El fundamento del estado
  2. useEffect – Efectos secundarios y ciclo de vida
  3. useContext – Compartir estado sin props
  4. useReducer – Lógica de Estado Compleja y predecible
  5. useRef – Referencias, Almacenamiento y Escape
  6. useMemo y useCallback – Optimizando el Rendimiento
  7. Custom Hooks – Creando Lógica Reutilizable
  8. Errores Comunes y Soluciones

🔹 1. useState – Manejar el estado local

¿Qué es y para qué sirve?

useState es el hook más fundamental. Te permite añadir una "variable de estado" a tus componentes de función. A diferencia de una variable normal, cuando el valor de esta variable cambia, React automáticamente vuelve a renderizar el componente para reflejar el cambio en la interfaz.

Úsalo para: manejar datos que cambian con el tiempo y que afectan a lo que se ve en pantalla, como el valor de un input, si un modal está abierto o los datos de un contador.

Sintaxis y parámetros

const [estado, setEstado] = useState(valorInicial);
Enter fullscreen mode Exit fullscreen mode
  • valorInicial: El valor que tendrá estado la primera vez que se renderice el componente. Puede ser cualquier tipo de dato (número, string, objeto, array, etc.).
  • estado: La variable que contiene el valor actual del estado. Es de solo lectura.
  • setEstado: La función que usas para actualizar el valor de estado. Al llamarla, se dispara una nueva renderización.

Ejemplo práctico detallado: formulario de registro

En lugar de un simple contador, veamos un ejemplo más realista: un formulario de registro que maneja un objeto como estado.

import React, { useState } from 'react';

function FormularioRegistro() {
  const [usuario, setUsuario] = useState({
    nombre: '',
    email: '',
    password: '',
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    // Usamos la forma funcional de setState para asegurar que tenemos el estado más reciente
    setUsuario(prevUsuario => ({
      ...prevUsuario,
      [name]: value,
    }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    // Aquí normalmente enviarías los datos a un servidor
    alert(`Usuario registrado: ${usuario.nombre} con email ${usuario.email}`);
    console.log(usuario);
  };

  return (
    <form onSubmit={handleSubmit}>
      <h3>Formulario de Registro</h3>
      <input
        type="text"
        name="nombre"
        placeholder="Nombre"
        value={usuario.nombre}
        onChange={handleChange}
      />
      <input
        type="email"
        name="email"
        placeholder="Email"
        value={usuario.email}
        onChange={handleChange}
      />
      <input
        type="password"
        name="password"
        placeholder="Contraseña"
        value={usuario.password}
        onChange={handleChange}
      />
      <button type="submit">Registrarse</button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

🚀 Ver el código interactivo en CodeSandbox Haz clic para abrir el sandbox y experimentar con el código

💡 Tip: Puedes modificar el código en el editor de la izquierda y ver los cambios en tiempo real.


✅ Buenas practicas y patrones comunes

  1. Agrupar estado relacionado: Si varios estados cambian juntos, considera agruparlos en un objeto o array. Esto es útil en formularios.
  2. Usar la actualización funcional: Cuando el nuevo estado depende del anterior (como en un contador), usa setContador(c => c + 1). Esto evita problemas con el estado "viejo" (stale state) en operaciones asíncronas.
  3. La actualización de estado es asíncrona: No esperes que el valor del estado se actualice inmediatamente después de llamar a setEstado. Si necesitas realizar una acción después de que el estado cambie, usa useEffect.
  4. No mutes el estado directamente: Especialmente con objetos y arrays. En lugar de usuario.nombre = 'nuevo', crea un nuevo objeto: setUsuario({...usuario, nombre: 'nuevo'}). La inmutabilidad es clave en React.

🚨 Errores comunes y cómo evitarlos

  • Error: Modificar el estado directamente.

    // MAL ❌
    const [user, setUser] = useState({ name: 'Ana' });
    user.name = 'Juan'; // No dispara re-render y es un anti-patrón
    setUser(user);
    
    • Solución: Siempre crea un nuevo objeto/array.
    // BIEN ✅
    setUser({ ...user, name: 'Juan' });
    
  • Error: Llamar a useState dentro de condicionales, bucles o funciones anidadas.

    • Solución: Los hooks siempre deben llamarse en el nivel superior del componente. React depende del orden en que se llaman para funcionar correctamente.

🚀 Retos prácticos

  • Contador con Límite: Crea un contador que no pueda pasar de 10 ni ser menor que 0.

💻 Código base (haz clic para mostrar)
import React, { useState } from "react";

// Reto: Contador con Límite
// Crea un contador que no pueda pasar de 10 ni ser menor que 0.
// Debe tener botones para incrementar, decrementar y resetear.

export default function ContadorLimiteReto(): React.ReactElement {
    // TODO: Implementar useState para el contador
    // const [contador, setContador] = useState<number>(0);

    // TODO: Implementar función incrementar
    // - Solo debe incrementar si el contador es menor que 10
    const incrementar = () => {
        // Tu código aquí
    };

    // TODO: Implementar función decrementar
    // - Solo debe decrementar si el contador es mayor que 0
    const decrementar = () => {
        // Tu código aquí
    };

    // TODO: Implementar función resetear
    // - Debe poner el contador en 0
    const resetear = () => {
        // Tu código aquí
    };

    return (
        <div style={{ padding: "20px", textAlign: "center" }}>
            <h1>Contador con Límite</h1>
            <div style={{ fontSize: "3rem", margin: "20px 0" }}>
                {/* TODO: Mostrar el valor del contador */}
                0
            </div>
            <div>
                <button
                    onClick={decrementar}
                    style={{ margin: "0 5px", padding: "10px 20px" }}
                // TODO: Deshabilitar si el contador es 0
                >
                    -
                </button>
                <button
                    onClick={resetear}
                    style={{ margin: "0 5px", padding: "10px 20px" }}
                >
                    Reset
                </button>
                <button
                    onClick={incrementar}
                    style={{ margin: "0 5px", padding: "10px 20px" }}
                // TODO: Deshabilitar si el contador es 10
                >
                    +
                </button>
            </div>
            <p style={{ marginTop: "20px", color: "#666" }}>
                Límites: 0 - 10
            </p>
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

  • Input con Validación: Haz un input que muestre un mensaje de error si el texto tiene menos de 5 caracteres.

💻 Código base (haz clic para mostrar)
import React, { useState } from "react";

// Reto: Input con Validación
// Haz un input que muestre un mensaje de error si el texto tiene menos de 5 caracteres.

export default function InputValidacionReto(): React.ReactElement {
    // TODO: Implementar useState para el texto del input

    // TODO: Crear una función que determine si el texto es válido

    return (
        <div style={{ padding: "20px", maxWidth: "400px" }}>
            <h1>Input con Validación</h1>

            <div style={{ marginBottom: "20px" }}>
                <label style={{ display: "block", marginBottom: "5px" }}>
                    Escribe al menos 5 caracteres:
                </label>
                <input
                    type="text"
                    // TODO: Conectar value y onChange con el estado
                    // value={_____}
                    // onChange={_____}
                    placeholder="Escribe aquí..."
                    style={{
                        width: "100%",
                        padding: "10px",
                        fontSize: "16px",
                        border: "2px solid #ccc", // TODO: Cambiar color según validación
                        borderRadius: "4px"
                    }}
                />
            </div>

            {/* TODO: Mostrar mensaje de error si el texto no es válido */}


            {/* TODO: Mostrar mensaje de éxito si el texto es válido */}

            <div style={{ marginTop: "20px", fontSize: "14px", color: "#666" }}>
                {/* TODO: Mostrar contador de caracteres */}
                Caracteres: 0/5
            </div>
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

  • Toggle de Visibilidad: Un botón que muestre u oculte un texto.

💻 Código base (haz clic para mostrar)
import React, { useState } from "react";

// Reto: Toggle de Visibilidad
// Un botón que muestre u oculte un texto.

export default function ToggleVisibilidadReto(): React.ReactElement {
    // TODO: Implementar useState para controlar la visibilidad

    // TODO: Implementar función para alternar la visibilidad
    const alternarVisibilidad = () => {
        // Tu código aquí
    };

    return (
        <div style={{ padding: "20px" }}>
            <h1>Toggle de Visibilidad</h1>

            <button
                onClick={alternarVisibilidad}
                style={{
                    padding: "10px 20px",
                    fontSize: "16px",
                    marginBottom: "20px"
                }}
            >
                {/* TODO: Cambiar el texto del botón según el estado */}
                Mostrar Texto
            </button>

            {/* TODO: Mostrar u ocultar el texto según el estado */}
            {/* {_____ && (
        <div style={{
          padding: "20px",
          backgroundColor: "#f0f0f0",
          borderRadius: "8px",
          border: "2px solid #ddd"
        }}>
          <h2>¡Texto Visible! 🎉</h2>
          <p>
            Este texto se muestra y oculta usando useState.
            Es un ejemplo perfecto de cómo controlar la renderización
            condicional en React.
          </p>
        </div>
      )} */}

            <div style={{ marginTop: "20px", fontSize: "14px", color: "#666" }}>
                {/* TODO: Mostrar el estado actual */}
                Estado actual: Oculto
            </div>
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

  • Lista de Tareas Simple: Un input para añadir tareas a una lista. Cada tarea debe tener un botón para eliminarla (pista: necesitarás manejar un array en el estado).

💻 Código base (haz clic para mostrar)
import React, { useState } from "react";

// Reto: Lista de Tareas Simple
// Un input para añadir tareas a una lista. Cada tarea debe tener un botón para eliminarla.
// Pista: necesitarás manejar un array en el estado.

interface Tarea {
    id: number;
    texto: string;
}

export default function ListaTareasReto(): React.ReactElement {
    // TODO: Implementar useState para la lista de tareas

    // TODO: Implementar useState para el input de nueva tarea

    // TODO: Implementar función para agregar tarea
    const agregarTarea = () => {
        // Verificar que el input no esté vacío
        // Crear nueva tarea con id único (puedes usar Date.now())
        // Agregar a la lista de tareas
        // Limpiar el input
    };

    // TODO: Implementar función para eliminar tarea
    const eliminarTarea = (id: number) => {
        // Filtrar la tarea con el id especificado
    };

    // TODO: Implementar función para manejar Enter en el input
    const manejarEnter = (e: React.KeyboardEvent) => {
        // Si la tecla es Enter, agregar la tarea
    };

    return (
        <div style={{ padding: "20px", maxWidth: "500px" }}>
            <h1>Lista de Tareas</h1>

            <div style={{ marginBottom: "20px", display: "flex", gap: "10px" }}>
                <input
                    type="text"
                    // TODO: Conectar value y onChange

                    placeholder="Escribe una nueva tarea..."
                    style={{
                        flex: 1,
                        padding: "10px",
                        fontSize: "16px",
                        border: "2px solid #ddd",
                        borderRadius: "4px"
                    }}
                />
                <button
                    onClick={agregarTarea}
                    style={{
                        padding: "10px 20px",
                        fontSize: "16px",
                        backgroundColor: "#4CAF50",
                        color: "white",
                        border: "none",
                        borderRadius: "4px",
                        cursor: "pointer"
                    }}
                >
                    Agregar
                </button>
            </div>

            {/* TODO: Renderizar la lista de tareas */}


            <div style={{ marginTop: "20px", fontSize: "14px", color: "#666" }}>
                {/* TODO: Mostrar contador de tareas */}
                Total de tareas: 0
            </div>
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)