A partir de React 16.8.0
hay nuevas formas de llamar al código de asíncrono de forma elegante, reutilizar la lógica entre componentes mucho más fácilmente.
Como desarrollador de React, es tu deber estar al día con sus nuevas características.
No para complacer a tu jefe, sino para mantenerte relevante en el campo y en el mercado.
Todavía recuerdo los viejos tiempos cuando nadie hablaba del patrón de redux y mis aplicaciones react eran un desastre de estado (mediados de 2014).
Cuando se introdujo el patrón de flujo al principio era difícil de entender y parece muy complicado de implementar, pero ahora unos años después es el estándar en todos los proyectos basados en el de react.
Con los hooks de react ocurrirá lo mismo, es el reemplazo de los componentes de clase y el futuro de react.js
Muy bien, este va a ser un largo post, así que he añadido una tabla de contenido para que podáis leer un poco, luego seguir trabajando en vuestro proyecto, y volver más tarde cuando necesitéis un descanso.
Soy el único que lee artículos técnicos para limpiar mi mente y liberar el estrés de mi trabajo diario?
Tabla de contenidos
- Qué son los React hooks de todos modos?
- React Hook vs React Class
- La existing React hooks
- Notation
- El useState hook
- El useEffect hook
- El useReducer hook
- El useRef hook
- Separación de intereses
- Casos de uso anticipado
- Ejemplos del mundo real
- Recursos increíbles
- Conclusión
Qué son los React hooks de todos modos? 🤔
Cuando trabajas con los componentes de la clase Reactjs puedes usar el estado, es por eso que estos componentes también se llaman stateful, también cada componente de la clase tiene métodos de ciclo de vida como: ComponentDidMount()
, ComponentDidUpdate()
, y así sucesivamente.
No puedes usar nada de esto en los componentes funcionales. Los componentes funcionales no pueden usar su propio estado y no tienen métodos de ciclo de vida.
Ahora con React hooks puedes.
Los React hooks nos permiten tomar un componente funcional de React.js y agregarle métodos de estado y ciclo de vida.
En palabras simples, los React hooks son funciones especiales para extender las capacidades de los componentes funcionales y darles la posibilidad de tener eventos de ciclo de vida y manejar el estado.
Comparemos cómo una clase difiere de un componente funcional cuando se usan React hooks
La buena y vieja manera de la clase
import React from 'react';
class ClickCounter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0 // Valor inicial para nuestro contador
};
}
setCount(numb) {
this.setState({
count: numb
})
}
render() {
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => this.setCount(this.state.count + 1).bind(this)}>
Click me
</button>
</div>
);
}
}
Con React hooks
import React, { useState } from 'react';
function ClickCounter() {
/**
useState crea una variable "count" que almacenará el estado y una función "setCount" que silenciará el estado de la variable "count"
**/
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Usando el ejemplo hooks "useState" para almacenar el estado en un componente de la función.
Menos líneas de código para hacer lo mismo!
Pero no es sólo eso, con los React hooks, ahora puedes reutilizar la lógica del estado y tener una mejor separación de las preocupaciones.
Al principio, esta nueva API puede parecerte extraña, pero quédate conmigo, aprenderás a sacarle el máximo provecho.
La existencia de React hooks 🍱
La nueva API viene con dos hooks preexistentes principales y algunos otros para otros casos de uso.
Lo esencial de React hooks
La base de todos los React hooks, todos los demás hooks que verán es una variación de estos tres o los están usando como primitivos.
-
El "useState" es el hook de State que se usa para declarar el estado en sus componentes
-
El "useEffect" es el hook de efectos laterales que se usa para obtener datos, cambiar manualmente el DOM, etc.
-
El "useContext" lo usa en conjunto con el API de Reactjs Context. Cuando el proveedor de React Context se actualice, este hook disparará el render con el último valor de contexto.
Los hooks de Advance React
Estos son los más importantes de los otros hooks incorporados de React que vienen con la biblioteca.
-
El "useReducer" es una alternativa al "useState", deberías usarlo cuando tengas lógica de estado compleja, si estás familiarizado con Redux te gustará.
-
El "useRef" lo usa para acceder a un elemento DOM con un objeto ref mutable. Es más útil que el atributo `ref
Esos peculiares paréntesis
Podrías preguntarte qué significa la sintaxis const [age, setAge] = useState(24)
,
pero es sólo la nueva forma de desestructurar una matriz, déjame mostrarte otra forma de hacerlo.
const ageStateVariable = useState(24); // Devuelve una tupla o una matriz de longitud 2
const age = ageStateVariable[0]; // Primer elemento
const setAge = ageStateVariable[1]; // Segundo elemento
// La forma en que el ES6 hace esto
const [age, setAge] = useState(24);
Me encantan las líneas simples y elegantes, no tanto como la gente pyton, y definitivamente NO me gustan las líneas locas como la gente pyton.
Reglas
-
Nunca llame a Hooks desde el interior de un bucle, condición o función anidada
-
Nunca llames a un Garfio desde una función regular
-
Sólo los llaman componentes de funciones internas o hooks personalizados
-
Los hooks deben estar en el nivel superior de su componente
-
Los hooks pueden llamar a otros hooks
El hook useState
🎲
El más fácil de usar y entender todos los hooks. Su propósito es almacenar el estado en un componente funcional.
Bueno, técnicamente no estamos almacenando el estado dentro de él, sino enganchando en el diccionario (llave-valor) de estados que son manejados por la biblioteca de reacciones bajo el capó. Pero no vamos a profundizar en esos detalles por ahora.
import React, { useState } from 'react';
function myAwesomeComponent () {
const [name, setName] = useState('John');
...
}
El useState devuelve una tupla con una propiedad state holder y un método setter.
Se invoca a useState con el valor inicial de su estado.
Para actualizar el estado llamas a la función "setName"
El hook useEffect
🍯
En una clase de React, normalmente se configura una suscripción en "ComponentDidMount", y se limpia en "ComponentWillUnmount".
Con el hook react "useEffect" lo realizamos devolviendo una función para limpiar o "desuscribir" el efecto.
Si has trabajado con mobx
este patrón puede resultarte familiar, es una analogía a una reacción.
useEffect(() => {
PlacesAPI.subscribeToPlaceNews(props.place.id, handlePlacesNews);
return () => {
PlacesAPI.unsubscribeFromPlaceNews(props.place.id, handlePlacesNews);
};
});
Why did we return a function from our effect?
This is the optional cleanup mechanism for effects. Every effect may return a function that cleans up after it.
This lets us keep the logic for adding and removing subscriptions close to each other.
El hook useReducer
🎣
Cuando tienes una lógica de estado compleja, es una buena idea usar un "reductor". Si estás familiarizado con librerías como "Redux" o el "flux pattern" lo entenderás a primera vista.
Básicamente con un reductor que "despacha" o desencadena algunas acciones en su vista, esos eventos son escuchados por un reductor que tiene la lógica dentro para actualizar la tienda que es donde vive su estado. Ahora, cuando el almacén se actualiza, tu componente se volverá a renderizar.
import React, { useReducer, useState } from 'react';
import produce from 'immer';
function reducer(state, action) {
switch (action.type) {
case 'toggle':
return produce(state, (draftState) => {
draftState[action.payload].isCompleted = !draftState[action.payload].isCompleted;
});
case 'add':
return produce(state, (draftState) => {
draftState.push({ label: action.payload });
});
default:
return state;
}
}
function Todo({ isCompleted, label, onChange }) {
return <p>
<label style={{
textDecoration: isCompleted && 'line-through'
}}>
<input
type="checkbox"
checked={isCompleted || false}
onChange={onChange}
/>
<span>{label}</span>
</label>
</p>
}
function TodoList() {
const todos = [
{ label: 'Do something' },
{ label: 'Buy dinner' }
];
const [state, dispatch] = useReducer(reducer, todos);
const [newTodo, setNewTodo] = useState('');
return <>
{state.map((todo, i) => (
<Todo
key={i}
{...todo}
onChange={() => dispatch({ type: 'toggle', payload: i })}
/>
))}
<input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
/>
<button onClick={() => {
dispatch({ type: 'add', payload: newTodo });
setNewTodo('');
}}>
Add
</button>
</>;
}
export default TodoList;
El hook useRef
🔮
Refs se utilizan para acceder a los elementos de React o a los elementos DOM renderizados en la función render.
El hook useRef
devuelve un objeto ref mutable cuya propiedad .current
se inicializa con el argumento pasado initialValue
.
Es muy simple de usar
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
Separación de intereses 🎎
Con Hooks, puedes extraer la lógica del estado de un componente para que pueda ser probado independientemente y reutilizado.
Los hooks permiten reutilizar la lógica del estado sin cambiar la jerarquía del componente.
Por ejemplo, los componentes pueden realizar algunas búsquedas de datos en "ComponentDidMount" y "ComponentDidUpdate".
Sin embargo, el mismo método de "ComponentDidMount" también puede contener lógica no relacionada que configura los escuchas de eventos, con la limpieza realizada en "ComponentWillUnmount".
El código mutuamente relacionado que cambia junto se divide, pero el código completamente no relacionado termina combinado en un solo método.
import React from 'react';
import PlacesAPI from '../services/place';
class PlaceNewsWithCounter extends React.Component {
constructor(props) {
super(props);
this.handlePlacesNews = this.handlePlacesNews.bind(this);
this.state = { count: 0, currentEvent: null };
}
// Lógica estatal no relacionada
componentDidMount() {
document.title = `You clicked ${this.state.count} times`;
PlacesAPI.subscribeToPlaceNews(
this.props.place.id,
this.handlePlacesNews
);
}
componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
}
componentWillUnmount() {
PlacesAPI.unsubscribeFromPlaceNews(
this.props.place.id,
this.handlePlacesNews
);
}
handlePlacesNews(place) {
this.setState({
currentEvent: place.currentEvent
});
}
...
}
Un mejor enfoque usando los React hooks
import React, { useState, useEffect } from 'react';
import PlacesAPI from '../services/place';
function PlaceNewsWithCounter() {
// Lógica para el contador aquí...
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
// Lógica para colocar la API aquí...
const [currentEvent, setCurrentEvent] = useState(null);
function handlePlacesNews(place) {
setCurrentEvent(place.currentEvent);
}
useEffect(() => {
PlacesAPI.subscribeToPlaceNews(props.place.id, handlePlacesNews);
return () => {
PlacesAPI.unsubscribeFromPlaceNews(props.place.id, handlePlacesNews);
};
});
return ...;
}
Advance use cases 🤵
Usando "useEffect" para la obtención de datos
Con la combinación de "useEffect" y "useState", puedes hacer llamadas a la API usando "useEffect" y pasando un array u objeto vacío como segundo argumento para tener el mismo comportamiento que componentDidMount.
La clave aquí es el segundo argumento. Si no proporcionas un array u objeto vacío como segundo argumento, la llamada a la API será llamada en cada render, y efectivamente se convierte en lo mismo que un componentDidUpdate
const [todo, setTodo] = useState(null);
const [id, setId] = useState(1);
useEffect(() => {
if (!id) {
return;
}
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
.then(results => results.json())
.then(data => {
setTodo(data);
});
}, [id]); // No te olvides de añadir esto!
Al pasar un segundo parámetro para usar el efecto, estamos estableciendo una suscripción cada vez que la propiedad cambia, el efecto se vuelve a activar.
Si en cambio, nos gustaría hacer una llamada a la API Sólo cuando el componente esté montado
const [fullName, setFullName] = useState(null);
useEffect(() => {
fetch('https://randomuser.me/api/')
.then(results => results.json())
.then(data => {
const {name} = data.results[0];
setFullName(`${name.first} ${name.last}`);
});
}, []); // <-- Tienes que pasar por [] aquí!
Ejemplos del mundo real
Mostrar estado en línea
Detectar el estado de conexión del dispositivo del usuario. (https://github.com/rehooks/online-status)_
Hook implementation
import { useEffect, useState } from "react";
function getOnlineStatus() {
return typeof navigator !== "undefined" &&
typeof navigator.onLine === "boolean"
? navigator.onLine
: true;
}
export const useOnlineStatus = () => {
let [onlineStatus, setOnlineStatus] = useState(getOnlineStatus());
const goOnline = () => setOnlineStatus(true);
const goOffline = () => setOnlineStatus(false);
useEffect(() => {
window.addEventListener("online", goOnline);
window.addEventListener("offline", goOffline);
return () => {
window.removeEventListener("online", goOnline);
window.removeEventListener("offline", goOffline);
};
}, []);
return onlineStatus;
}
Uso de Hook
const App = () => {
let onlineStatus = useOnlineStatus();
return (
<div>
<h1>You are {onlineStatus ? "Online" : "Offline"}</h1>
</div>
);
}
Detectar cambios de geolocalización
Rastrea el estado de geolocalización del dispositivo del usuario. (https://github.com/streamich/react-use)_
Implementación de Hook
import {useState, useEffect} from 'react';
const useGeolocation = () => {
const [state, setState] = useState({
loading: true,
accuracy: null,
altitude: null,
altitudeAccuracy: null,
heading: null,
latitude: null,
longitude: null,
speed: null,
timestamp: Date.now(),
});
let mounted = true;
let watchId: any;
const onEvent = (event: any) => {
if (mounted) {
setState({
loading: false,
accuracy: event.coords.accuracy,
altitude: event.coords.altitude,
altitudeAccuracy: event.coords.altitudeAccuracy,
heading: event.coords.heading,
latitude: event.coords.latitude,
longitude: event.coords.longitude,
speed: event.coords.speed,
timestamp: event.timestamp,
});
}
};
const onEventError = (error: any) =>
mounted && setState(oldState => ({...oldState, loading: false, error}));
useEffect(() => {
navigator.geolocation.getCurrentPosition(onEvent, onEventError);
watchId = navigator.geolocation.watchPosition(onEvent, onEventError);
return () => {
mounted = false;
navigator.geolocation.clearWatch(watchId);
};
}, [0]);
return state;
};
Hook Usage
const Demo = () => {
const state = useGeolocation();
return (
<pre>
{JSON.stringify(state, null, 2)}
</pre>
);
};
Proyectos impresionantes ✨
-
react-use The largest collection of React hooks, really worth checking out.
-
awesome react hooks Curated list of awesome resources for learning React hooks.
-
react-swipeable Provides swipe events for your component.
-
easy-peasy Global state manager powered by React hooks.
-
react-intersection-observer Tell you when an element enters or leaves the viewport.
-
UseHooks.com Collection of recipes for several React hooks.
Conclusión 🎉
La nueva API de React hook es un cambio de juego, ahora podemos usar el estado en los componentes de función, reutilizar la lógica del estado.
Aprendemos sobre "usar el estado" y "usar el efecto", esos son los primitivos para cada gancho que verás.
**Recuerda, cada nuevo gancho es una derivación de uno de esos dos.
Hablamos de que otros reaccionan con hooks incorporados como "useReducer" y "useRef".
Creamos nuestros propios hooks personalizados para manejar la obtención de datos e implementamos nuestra propia versión de useReducer
para demostrar la magia que hay detrás.
Mantengan la calma y continúen aprendiendo
Recursos
- https://reactjs.org/docs/hooks-intro.html
- https://stackoverflow.com/questions/53219113/where-can-i-make-api-call-with-hooks-in-react
- https://www.robinwieruch.de/react-hooks-fetch-data/
- https://stackoverflow.com/questions/53332321/react-hook-warnings-for-async-function-in-useeffect-useeffect-function-must-ret
- https://usehooks.com/
- https://rehooks.com/
- https://css-tricks.com/intro-to-react-hooks/