React
Tutorial React: Introducción a los Hooks
React Introducción a los hooks en componentes funcionales. Aprende uso de useState, useEffect y más. Mejora tus habilidades en React sin componentes de clase.
Aprende React GRATIS y certifícate¿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>
);
}
// Correcto
// 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
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 usaruseEffect
.
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.
Ejercicios de esta lección Introducción a los Hooks
Evalúa tus conocimientos de esta lección Introducción a los Hooks con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Props y manejo de datos entre componentes
Definición y manejo de rutas
Conocimiento general de React
Galería de imágenes en React
Eventos en React
Gestor de tareas con React
Custom Hooks para servicios compartidos
Nuevos métodos create de React Router
Solicitudes HTTP con Fetch API
Instalar React y crear nuevo proyecto
Renderizado condicional
Introducción a JSX
Manejo de clases y estilos
Introducción a React Router
Solicitudes HTTP con Axios
Estado local con useState y useReducer
Estado global con Redux Toolkit
Estado y ciclo de vida de los componentes
Hooks para gestión de estado complejo y contexto
Componentes funcionales
Estado global con Context API
Hooks: optimización y concurrencia
Introducción a React y su ecosistema
Introducción a Componentes
Introducción a los Hooks
Navegación programática y redirección
Renderizado iterativo con bucles
Rutas anidadas y rutas dinámicas
Hooks: estado y efectos secundarios
Todas las lecciones de React
Accede a todas las lecciones de React y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A React Y Su Ecosistema
Introducción Y Entorno
Instalar React Y Crear Nuevo Proyecto
Introducción Y Entorno
Introducción A Jsx
Componentes
Introducción A Componentes
Componentes
Componentes Funcionales
Componentes
Eventos En React
Componentes
Props Y Manejo De Datos Entre Componentes
Componentes
Renderizado Condicional
Componentes
Renderizado Iterativo Con Bucles
Componentes
Manejo De Clases Y Estilos
Componentes
Introducción A Los Hooks
Hooks
Estado Y Ciclo De Vida De Los Componentes
Hooks
Hooks Estado Y Efectos Secundarios
Hooks
Hooks Para Gestión De Estado Complejo Y Contexto
Hooks
Hooks Optimización Y Concurrencia
Hooks
Introducción A React Router
Navegación Y Enrutamiento
Definición Y Manejo De Rutas
Navegación Y Enrutamiento
Rutas Anidadas Y Rutas Dinámicas
Navegación Y Enrutamiento
Navegación Programática Redirección
Navegación Y Enrutamiento
Nuevos Métodos Create De React Router
Navegación Y Enrutamiento
Solicitudes Http Con Fetch Api
Interacción Http Con Backend
Solicitudes Http Con Axios
Interacción Http Con Backend
Estado Local Con Usestate Y Usereducer
Servicios Y Gestión De Estado
Estado Global Con Context Api
Servicios Y Gestión De Estado
Estado Global Con Redux Toolkit
Servicios Y Gestión De Estado
Custom Hooks Para Servicios Compartidos
Servicios Y Gestión De Estado
Evaluación Test React
Evaluación
Certificados de superación de React
Supera todos los ejercicios de programación del curso de React y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.
En esta lección
Objetivos de aprendizaje de esta lección
- 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
yuseRef
en proyectos prácticos.