Introducción a los Hooks

Intermedio
React
React
Actualizado: 22/05/2025

¡Desbloquea el curso de React completo!

IA
Ejercicios
Certificado
Entrar

Mira la lección en vídeo

Accede al vídeo completo de esta lección y a más contenido exclusivo con el Plan Plus.

Desbloquear Plan Plus

¿Qué son los hooks?

Los hooks son una característica de React que permite usar el estado y otras características de React en componentes funcionales, sin necesidad de escribir una clase. Introducidos en React 16.8, los hooks permiten manejar efectos secundarios, estado y otros aspectos de los componentes de una manera más sencilla y funcional.

Reglas de los hooks

Los hooks de React siguen un conjunto de reglas estrictas que aseguran que funcionen correctamente y de manera predecible. Estas reglas son fundamentales para mantener la consistencia y evitar errores difíciles de depurar. A continuación, se detallan las reglas que deben seguirse al utilizar hooks en React.

Llamar a los hooks en el nivel superior

Los hooks deben ser llamados en el nivel superior de la función del componente. No deben ser llamados dentro de bucles, condiciones o funciones anidadas. Esta regla asegura que los hooks se llamen en el mismo orden en cada renderizado, lo cual es crucial para que React pueda mantener correctamente el estado de los hooks entre renderizados.

// Correcto
// MyComponent.jsx
import { useState, useEffect } from 'react';

export default function MyComponent() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>Incrementar</button>
    </div>
  );
}

// Incorrecto
// MyComponent.jsx
import { useState, useEffect } from 'react';

export default function MyComponent() {
  const [count, setCount] = useState(0);

  if (count > 0) {
    useEffect(() => {
      document.title = `Count: ${count}`;
    }, [count]); // Esto romperá las reglas de los hooks
  }

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>Incrementar</button>
    </div>
  );
}

Llamar a los hooks únicamente desde componentes funcionales o hooks personalizados

Los hooks solo deben ser llamados desde componentes funcionales o desde otros hooks personalizados. No deben ser llamados desde funciones regulares de JavaScript. Esta regla permite que React pueda gestionar el estado y el ciclo de vida de los hooks correctamente.

// Correcto
// useCustomHook.js
import { useState } from "react";

export function useCustomHook() {
  const [value, setValue] = useState(0);
  return [value, setValue];
}

// MyComponent.jsx
import { useCustomHook } from './hooks/useCustomHook';

export function MyComponent() {
  const [value, setValue] = useCustomHook();
  return (
    <div>
      <p>{value}</p>
      <button onClick={() => setValue(value + 1)}>Incrementar</button>
    </div>
  );
}

// Incorrecto
// regularFunction.js
import { useState } from "react";

export function regularFunction() {
  const [value, setValue] = useState(0); // Esto romperá las reglas de los hooks
}

Usar hooks personalizados para encapsular lógica de estado y efectos

Los hooks personalizados permiten encapsular y reutilizar lógica de estado y efectos entre múltiples componentes. Deben seguir las mismas reglas que los hooks integrados. Al crear un hook personalizado, asegúrate de que el nombre de la función comience con use para que React pueda identificarlo como un hook.

// useWindowWidth.js
import { useState, useEffect } from 'react';

export function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);

    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return width;
}

// DisplayWidth.jsx
import { useWindowWidth } from "../../hooks/useWindowWidth";

export default function DisplayWidth() {
  const width = useWindowWidth();

  return <div>El ancho de la ventana es: {width}px</div>;
}

Cumplir con estas reglas es esencial para asegurar que los hooks funcionen de manera predecible y eficiente. La adherencia a estas reglas permite a React optimizar el rendimiento y la consistencia del estado en los componentes funcionales.

Vista general introductoria de todos los hooks

Guarda tu progreso

Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.

Progreso guardado
Asistente IA
Ejercicios
Iniciar sesión gratis

Más de 25.000 desarrolladores ya confían en CertiDevs

React proporciona una serie de hooks integrados que permiten manejar diferentes aspectos de los componentes funcionales. Cada hook tiene un propósito específico y puede ser utilizado para gestionar el estado, efectos secundarios, referencias y más.

useState: Permite añadir estado local a un componente funcional. Recibe un valor inicial y devuelve un array con dos elementos: el estado actual y una función para actualizarlo.

// Counter.jsx
import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Has hecho clic {count} veces</p>
      <button onClick={() => setCount(count + 1)}>Haz clic</button>
    </div>
  );
}

useEffect: Permite ejecutar efectos secundarios en componentes funcionales. Recibe una función que se ejecutará después de que el renderizado del componente sea completado. También puede recibir un array de dependencias que determina cuándo se debe volver a ejecutar el efecto.

// Timer.jsx
import { useState, useEffect } from 'react';

export default function Timer() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => {
      setCount(prevCount => prevCount + 1);
    }, 1000);

    return () => clearInterval(timer);
  }, []);

  return <div>Segundos: {count}</div>;
}

useContext: Permite acceder al contexto de React. Es útil para compartir datos que deben ser accesibles por muchos componentes sin necesidad de pasar props manualmente por cada nivel del árbol de componentes.

// ThemeContext.jsx
import { createContext, useContext } from 'react';

const ThemeContext = createContext('light');

export function ThemeProvider({ children }) {
  return (
    <ThemeContext.Provider value="dark">
      {children}
    </ThemeContext.Provider>
  );
}

export function ThemedComponent() {
  const theme = useContext(ThemeContext);
  return <div>El tema actual es {theme}</div>;
}

// App.jsx
import { ThemedComponent, ThemeProvider } from './components/ThemedComponent/ThemeContext';

export default function App() {
  return (
    <ThemeProvider>
      <ThemedComponent />
    </ThemeProvider>
  );
}

useReducer: Similar a useState, pero más adecuado para gestionar estados complejos que involucran múltiples subvalores. Se usa con un reducer, una función pura que toma el estado actual y una acción, y devuelve un nuevo estado.

// CounterReducer.jsx
import { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

export default function CounterReducer() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <div>
      <p>Contador: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Incrementar</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrementar</button>
    </div>
  );
}

useCallback: Memoriza funciones para evitar recrearlas en cada renderizado, lo cual es útil para optimizar el rendimiento de componentes que dependen de funciones que se pasan como props.

// CallbackComponent.jsx
import { useState, useCallback } from 'react';

export default function CallbackComponent() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
    setCount(prevCount => prevCount + 1);
  }, []);

  return (
    <div>
      <p>Contador: {count}</p>
      <button onClick={increment}>Incrementar</button>
    </div>
  );
}

useMemo: Memoriza valores calculados para evitar recalculaciones en cada renderizado. Recibe una función y un array de dependencias.

// ExpensiveCalculation.jsx
import { useState, useMemo } from 'react';

const expensiveCalculation = (num) => {
  console.log('Calculando...');
  for (let i = 0; i < 1000000000; i++) { } // Simulación de cálculo costoso
  return num * 2;
};

export default function ExpensiveCalculation() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');

  const memoizedValue = useMemo(() => expensiveCalculation(count), [count]);

  return (
    <div>
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Escribe algo"
      />
      <p>Texto: {text}</p>
      <p>Contador: {count}</p>
      <p>Resultado del cálculo: {memoizedValue}</p>
      <button onClick={() => setCount(count + 1)}>Incrementar contador</button>
    </div>
  );
}

useRef: Permite crear una referencia mutable que no provoca un nuevo renderizado cuando cambia. Es útil para acceder y manipular elementos del DOM directamente. A diferencia de useState, los cambios en useRef no afectan la salida visual del componente.

// FocusInput.jsx
import { useRef } from 'react';

export default function FocusInput() {
  const inputRef = useRef(null);

  const handleClick = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={handleClick}>Enfocar Input</button>
    </div>
  );
}

useLayoutEffect: Similar a useEffect, pero se ejecuta de manera sincrónica después de todas las mutaciones del DOM. Generalmente se usa para leer el layout del DOM y sincronizarlo antes de que se pinte en la pantalla.

// LayoutEffectComponent.jsx
import { useState, useLayoutEffect, useRef } from 'react';

export default function LayoutEffectComponent() {
  const [width, setWidth] = useState(0);
  const divRef = useRef(null);

  useLayoutEffect(() => {
    setWidth(divRef.current.offsetWidth);
  }, []);

  return (
    <div ref={divRef}>
      <p>El ancho del div es: {width}</p>
    </div>
  );
}

Advertencia: El código dentro de useLayoutEffect y todas las actualizaciones de estado programadas desde él bloquean el navegador de volver a pintar en la pantalla. Cuando es usado excesivamente, puede hacer tu aplicación muy lenta. Cuando sea posible se prefiere usar useEffect.

Estos hooks proporcionan una manera flexible y eficiente de manejar el estado y los efectos secundarios en componentes funcionales de React, permitiendo una mayor modularidad y reutilización de la lógica de los componentes.

Limitaciones de los componentes de clase

Los componentes de clase en React han sido una herramienta fundamental desde los inicios de la biblioteca, pero presentan varias limitaciones que han llevado a la introducción y adopción de los hooks en componentes funcionales. A continuación, se exploran algunas de estas limitaciones:

Complejidad en la gestión del estado y efectos secundarios

En los componentes de clase, la gestión del estado y los efectos secundarios puede volverse complicada y difícil de seguir. Los métodos de ciclo de vida (componentDidMount, componentDidUpdate, componentWillUnmount) a menudo contienen código mezclado que maneja diferentes aspectos del estado y los efectos secundarios, lo que puede hacer que el código sea menos legible y más propenso a errores.

// ClaseComponent.jsx
import { Component } from 'react';

export default class ClaseComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
      width: window.innerWidth
    };
  }

  componentDidMount() {
    document.title = `Count: ${this.state.count}`;
    window.addEventListener('resize', this.handleResize);
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevState.count !== this.state.count) {
      document.title = `Count: ${this.state.count}`;
    }
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.handleResize);
  }

  handleResize = () => {
    this.setState({ width: window.innerWidth });
  };

  incrementCount = () => {
    this.setState((prevState) => ({ count: prevState.count + 1 }));
  };

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.incrementCount}>Increment</button>
        <p>Window width: {this.state.width}</p>
      </div>
    );
  }
}

Reutilización de lógica de estado

Reutilizar la lógica de estado y efectos secundarios en componentes de clase es complicado y generalmente requiere patrones como componentes de orden superior (Higher-Order Components, HOCs) o render props. Estos patrones pueden aumentar la complejidad y el número de capas en la jerarquía de componentes, haciendo que el código sea más difícil de seguir y mantener.

// ClaseComponent.jsx
import { Component } from 'react';

export default class ClaseComponent extends Component {
  render() {
    const { width } = this.props;
    return (
      <div>
        <p>Width: {width}</p>
      </div>
    )
  }
}

// withWindowWidth.jsx (HOC)
import { Component } from 'react';

export function withWindowWidth(WrappedComponent) {
  return class extends Component {
    state = { width: window.innerWidth };

    handleResize = () => {
      this.setState({ width: window.innerWidth });
    };

    componentDidMount() {
      window.addEventListener('resize', this.handleResize);
    }

    componentWillUnmount() {
      window.removeEventListener('resize', this.handleResize);
    }

    render() {
      const { width } = this.state;
      return <WrappedComponent width={width} {...this.props} />;
    }
  };
}

// App.jsx
import ClaseComponent from './components/ClaseComponent/ClaseComponent';
import { withWindowWidth } from './components/withWindowWidth/withWindowWidth';

const HofComponent = withWindowWidth(ClaseComponent);

export default function App() {
  return (
    <HofComponent />
  );
}

Dificultad para dividir la lógica por funcionalidad

En componentes de clase, la lógica se agrupa por métodos de ciclo de vida en lugar de por funcionalidad. Esto puede hacer que la lógica relacionada, como la configuración de un suscriptor y su limpieza, esté dispersa en varios métodos, dificultando la comprensión y el mantenimiento del código.

// ClaseComponent.jsx
import { Component } from 'react';

export default class ClaseComponent extends Component {
  state = { count: 0 };

  componentDidMount() {
    this.timer = setInterval(() => this.setState({ count: this.state.count + 1 }), 1000);
  }

  componentWillUnmount() {
    clearInterval(this.timer);
  }

  render() {
    return <div>Count: {this.state.count}</div>;
  }
}

Problemas de rendimiento y optimización

Los componentes de clase pueden tener problemas de rendimiento debido a la recreación de funciones en cada renderizado. Aunque se pueden utilizar métodos como shouldComponentUpdate y PureComponent para optimizar, esto añade complejidad al código y no siempre es intuitivo.

// ClaseComponent.jsx
import { PureComponent } from 'react';

export default class ClaseComponent extends PureComponent {
  state = { count: 0 };

  incrementCount = () => {
    this.setState((prevState) => ({ count: prevState.count + 1 }));
  };

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.incrementCount}>Increment</button>
      </div>
    );
  }
}

Dificultad en la prueba y depuración

Los componentes de clase pueden ser más difíciles de probar y depurar debido a la necesidad de simular el ciclo de vida del componente y el estado. Los hooks permiten una manera más directa y funcional de manejar el estado y los efectos, lo que facilita la prueba y depuración del código.

Estas limitaciones de los componentes de clase han llevado a la adopción de hooks en componentes funcionales, proporcionando una forma más sencilla y modular de manejar el estado y los efectos secundarios en React.

Aprendizajes de esta lección de React

  • Comprender qué son los hooks y cómo funcionan en React.
  • Aprender las reglas fundamentales para usar hooks de manera correcta.
  • Implementar useState para añadir estado a los componentes funcionales.
  • Utilizar useEffect para manejar efectos secundarios en componentes.
  • Desarrollar hooks personalizados para encapsular lógica reutilizable.
  • Reconocer las limitaciones de los componentes de clase y cómo los hooks las superan.
  • Aplicar otros hooks como useContext, useReducer, useCallback, useMemo y useRef en proyectos prácticos.

Completa este curso de React y certifícate

Únete a nuestra plataforma de cursos de programación y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.

Asistente IA

Resuelve dudas al instante

Ejercicios

Practica con proyectos reales

Certificados

Valida tus conocimientos

Más de 25.000 desarrolladores ya se han certificado con CertiDevs

⭐⭐⭐⭐⭐
4.9/5 valoración