Los React Hooks llegaron en la versión 16.8 y han traído un montón de ventajas para trabajar de una forma mucho más cómoda. En particular porque ahora tenemos una alternativa a los class components, la cual son los functional components para manejar el estado y cambios en nuestra aplicación de una forma más fácil.
Uno de sus efectos colaterales es que abrieron un mundo de posibilidades, ya que podemos abstraer la lógica de nuestros componentes creando tus propios custom hooks.
Para dejar bien en claro que son, la propia documentación de React los define de la siguiente forma:
Un Hook personalizado es una función de JavaScript cuyo nombre comienza con ”
use
” y que puede llamar a otros Hooks
Algo que me gustaría compartir con todo aquel que este interesado en este tema, es una colección de custom hooks que les pueden servir en sus proyectos profesionales.
Conoce más sobre: memoization javascript
La mayoría de veces los protagonistas serán los hooks de estado y efecto, aunque puedes incluir otros si así lo necesitas.
Teniendo en cuenta lo anterior, puedes empezar a analizar cuándo es que realmente requieres crear un custom hook. Un ejemplo que puede pasar desapercibido cuando estás desarrollando es el siempre repetir código para consumir una API en dos o más componentes de tus aplicaciones, estas son esas áreas de oportunidad para mejorar tu código con esta práctica.
En lugar de tener 2 componentes con la siguiente estructura:
// ./components/Characters.jsximport React, { useState, useEffect } from'react'exportconst PrincipalCharacters = () => {
const [data, setData] = useState([])
useEffect(() => {
fetch("https://example.com/api/principal")
.then(data => data.json())
.then(json => setData(json))
}, [])
return (
<div>
...
</div>
)
}
exportconst ExtraCharacters = () => {
const [data, setData] = useState([])
useEffect(() => {
fetch("https://example.com/api/extra")
.then(data => data.json())
.then(json => setData(json))
}, [])
return (
<div>
...
</div>
)
}
Podrías crear un custom hook cuya función solo sea el consumir la API proveída, devolver un objeto con la data extraída y un estado que indique si se está cargando o no (para mostrar un componente que le indique al usuario esta acción si es el caso).
// ./hooks/usApi.jsimport React, {useState, useEffect} from'react'// Recuerda siempre usar la palabra "use" al principio de cada custom hookexportconst useApi = (API) => {
const [loading, setLoading] = useState(false)
const [data, setData] = useState()
useEffect(() => {
setLoading(true)
fetch(API)
.then(data => data.json())
.then(json => {
setData(json)
setLoading(false)
})
}, [])
return {loading, data}
}
De esta forma se reduce una considerable cantidad de líneas de código en ambos componentes:
// ./components/Characters.jsximport React from'react'import { useApi } from'@hooks/useApi.js'exportconst PrincipalCharacters = () => {
const {loading, data} = useApi("https://example.com/api/principal")
return (
<div>
...
</div>
)
}
exportconst ExtraCharacters = () => {
const {loading, data} = useApi("https://example.com/api/extra")
return (
<div>
...
</div>
)
}
¿Ves lo limpio que quedan los componentes abstrayendo la lógica repetitiva?
💡 Tip:
No hay una regla establecida sobre cuantos parámetros pasar a un custom hook, por lo que puedes encontrarte con algunos que solo devuelvan valores tal como sería el caso deuseGeolocation
que se ve aquí abajo.
Este hook, a diferencia de los siguientes, puedes optar por no usar el objeto de configuración y llamarlo para obtener los valores de position y error.
Usa este hook cuando necesites obtener la ubicación de tus usuarios usando la API de localización (recuerda que aún puedes mejorarlo haciendo un par de validaciones).
import { useEffect, useState } from'react';
functionuseGeolocation(options) {
const [position, setPosition] = useState();
const [error, setError] = useState();
useEffect(() => {
let canceled = false;
navigator.geolocation.getCurrentPosition(
(position) => {
if (!canceled) {
setPosition(position);
}
},
(error) => {
if (!canceled) {
setError(error);
}
},
options
);
return () => {
canceled = true;
};
}, [options]);
return [position, error];
}
Cuando desarrollas con React, lo más común es dejarle el trabajo de decidir cuándo sí y cuándo no hacer un re-render de nuestros componentes. Este hook ayuda a forzar un re-render de forma manual cuando es llamado.
import { useReducer } from'react';
const updateReducer = (num) => (num + 1) % 1_000_000;
functionuseUpdate(){
const [, update] = useReducer(updateReducer, 0);
return update;
}
Ya vimos como JuanDC en el Curso de React.js nos dio una cátedra de cómo abstraer lógica para crear este hook, así que no podría quedar fuera de esta serie de recomendaciones:
import React, { useState } from'react'functionuseLocalStorage(itemName, initialValue){
const localStorageItem = localStorage.getItem(itemName);
let parsedItem;
if (!localStorageItem) {
localStorage.setItem(itemName, JSON.stringify(initialValue));
parsedItem = initialValue;
} else {
parsedItem = JSON.parse(localStorageItem);
}
const [item, setItem] = useState(parsedItem);
const saveItem = (newItem) => {
const stringifiedItem = JSON.stringify(newItem);
localStorage.setItem(itemName, stringifiedItem);
setItem(newItem);
};
return [ item, saveItem ];
}
Dentro del navegador existen múltiples API’s y una de las que a mi parecer es de las más interesantes es la del intersectionObserver
, cuya funcionalidad es notificar cuando un elemento está dentro o fuera de la pantalla. Este hook ayuda a emplearlo de manera sencilla, pasándole la referencia de nuestro elemento y un objeto de configuración propio del intersectionObserver de manera opcional.
import { useEffect, useState } from'react';
functionuseIntersectionObserver(target, options) {
const [entries, setEntries] = useState([]);
useEffect(() => {
const observer = new IntersectionObserver(
entries => setEntries(entries),
options
);
observer.observe(target.current);
return () => {
observer.unobserve(target.current);
};
}, [options, target]
);
return entries;
}
Es bien sabido que las SPA no tienen un gran SEO, por lo que debes apoyarte de dependencias como React helmet para mejorar este aspecto, aunque este hook no lo reemplaza, es de gran ayuda cuando no es de tanta importancia pero tienes la intención de mostrarle al usuario que cambió de vista con esta ligera actualización en el document title
import React, { useRef, useEffect } from'react';
functionuseDocumentTitle(title, retainOnUnmount = false) {
const defaultTitle = useRef(document.title);
useEffect(() => {
document.title = title;
}, [title]);
useEffect(() => {
return () => {
if (!retainOnUnmount) {
document.title = defaultTitle.current;
}
};
}, []);
}
Recuerda que React tiene un ciclo de vida, el cual lo hace poder ser “reactivo” y responder a los cambios que existen dentro de tus aplicaciones. No puedes simplemente alterar el valor de tus variables en cualquier lugar de tu código, sino que debes notificárselo a React mediante el hook de efecto y estado.
Si tienes alguna duda más, puedes dejarla aquí abajo en la sección de comentarios y trataré de responderte lo más rápido posible.
Dentro de Platzi hay una gran cantidad de contenido referente a este framework, aquí tienes una gran saga de cursos que te ayudarán a convertirte en un maestro o maestra Jedi con React:
¿Te animas a hacerlos? 👀
#NuncaParesDeAprender
Muy útiles todos, me parece más divertido React desde que apostó por los functional components y los hooks.
Concuerdo! hicieron que React fuera más fácil de aprender y abrieron un mundo de posibilidades
Muy buena, yo agregaria otro customHook para medir el tamaño de pantalla y poder hacer optimizaciones desde JS y no hacerlo todo con CSS
Súper interesante la propuesta, aunque siento que sería dejarle mucho trabajo a JS. Pero podríamos comparar que tanto mejoran nuestras SPA manejándolo con un custom hook o con css puro
@LeoCode0 naaa, ese hook es como si fuera un if, es mas costoso para el navegador leer todo el css (hasta donde tengo entendido jaja)
Los hooks son lo mejor de React 😍
Y nos dejan la puerta abierta para hacer un montón de combinaciones!
Vamos a ver que trae de nuevo la siguiente versión 👀
Muchas gracias LeoCode0, espectacular!