Avanzando: Hooks de React
Introducción
Ya hemos dicho que a partir de un cierto momento React pretende que todo se resuelva con funciones y no con clases. Un ejemplo concreto es el uso de estos hooks para resolver problemas muy comunes que tienen que ver con las variables y el contexto de la app (ciclo de vida de un sistema).
Sin embargo, todo nuestro conocimiento de JavaScript es totalmente funcional y de hecho no hace falta utilizar estos hooks para resolver un problema, simplemente son soluciones (elegantes) para problemas recurrentes.
De esta manera los React Hooks son funciones simples de JavaScript que podemos usar para aislar la parte reutilizable de un componente funcional. Los hooks pueden tener estado y pueden gestionar los efectos secundarios de sus acciones. Los hooks son funciones que le permiten "enganchar" el estado de React y las características del ciclo de vida de los componentes de la función. Los hooks no funcionan dentro de las clases.
Hay muchos hooks incorporados al estandar de React (incluso puedes crear uno propio):
- useState: para gestionar estados. Devuelve un valor con estado y una función de actualización para actualizarlo.
- useEffect: para administrar efectos secundarios como llamadas a una API, suscripciones, temporizadores, mutaciones y más.
- useContext.
- useReducer.
- useId.
- y muchos otros...
Aquí hablaremos de solo dos.
useState
useState es un hook que lo llamamos dentro de un componente de función para agregarle algún estado local. React conservará este estado entre renderizaciones,
useState devuelve un par ordenado: el valor del estado actual y una función que le permite actualizarlo. Puede llamar a esta función desde un controlador de eventos o desde otro lugar. Es similar a this.setState en una clase, excepto que no fusiona el estado antiguo y el nuevo.
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Este el código para crear un botón que cuando se haga click incremente el valor de un contador.
El único argumento para useState es su estado inicial, que también indica el tipo de dato con el que estamos trabajando. En el ejemplo anterior, es 0 porque nuestro contador comienza desde cero (tenga en cuenta que esto determina el tipo de dato de la variable count). Tenga en cuenta que, a diferencia de this.state, el estado aquí no tiene que ser un objeto, aunque puede serlo si lo desea. El argumento de estado inicial solo se usa durante el primer renderizado. Luego, en la función onClick() del botón se llama a setCount para actualizar el valor de la variable.
useEffect
El ciclo de vida de los componentes en React, en versiones anteriores, permitía en los componentes con class ejecutar código en diferentes fases de montaje, actualización y desmontaje (componentDidMount, componentDidUpdate, and componentWillUnmount). De esta forma, podíamos añadir cierta funcionalidad en las distintas etapas de nuestro componente.
Con los hooks también podremos acceder a esa ciclo de vida en nuestros componentes funcionales aunque de una forma más clara y sencilla. Para ello usaremos useEffect, un hook que recibe como parámetro una función que se ejecutará cada vez que nuestro componente se renderice, ya sea por un cambio de estado, por recibir props nuevas o, y esto es importante, porque es la primera vez que se monta.
Por ejemplo, este componente determina el título de un documento después de actualizar el DOM:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`; });
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Con solo ingresar a la página se ejecuta useEffect al montarse nuestro componente. Luego cada vez que hacemos click en el componente, cuando el state cambia, esto dispara un nuevo renderizado y, al renderizarse de nuevo (actualización del DOM), genera como efecto que se vuelve a ejecutar la función que le hemos pasado a useEffect.
Es importante enteder que hay dos tipos de efectos secundarios en los componentes de React: aquellos que no necesitan una operación de saneamiento y los que sí la necesitan.
Observen que en el ejemplo anterior, el efecto de cambiar el título de la página no requiere una acción de saneamiento, decimos esto porque se ejecuta y no necesita ninguna nueva acción sobre esto, podemos olvidarnos de ello inmediatamente.
Sin embargo, existen otros efectos que necesitan
Si tu efecto devuelve una función, React la ejecutará en el momento de sanear el efecto:
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
¿Por qué hemos devuelto una función en nuestro efecto (cleanup)? Este es un mecanismo opcional de los efectos. Todos los efectos pueden devolver una función que los sanea más tarde, si fuera necesario. Esto nos permite mantener la lógica de añadir y eliminar suscripciones cerca la una de la otra. ¡Son parte del mismo efecto!
¿Cuándo sanea React el efecto exactamente? React sanea el efecto cuando el componente se desmonta. Sin embargo, como hemos aprendido anteriormente, los efectos no se ejecutan solo una vez, sino en cada renderizado. He aquí el motivo por el cual React también sanea los efectos de renderizados anteriores antes de ejecutar los efectos del renderizado actual.