Mejores prácticas con ganchos de reacción
Publicado: 2022-03-10 Los Hooks de React son una nueva adición en React 16.8 que te permiten usar el estado y otras características de React sin escribir un componente de class
. En otras palabras, los Hooks son funciones que le permiten "engancharse" a las características de estado y ciclo de vida de React de los componentes de la función. (No funcionan dentro de los componentes class
).
React proporciona algunos ganchos integrados como useState
. También puede crear sus propios Hooks para reutilizar el comportamiento con estado entre diferentes componentes. El siguiente ejemplo muestra un contador cuyo estado se administra mediante el gancho useState()
. Cada vez que hace clic en el botón, usamos setCount()
para actualizar el valor de count
en 1
.
Este ejemplo representa un contador con un valor de 0
. Cuando hace clic en el botón, incrementa el valor en 1
. El valor inicial del componente se define mediante useState
.
const [count, setCount] = useState(0)
Como puede ver, establecemos que sea 0
. Luego usamos el método onClick()
para llamar a setCount
cuando queremos incrementar el valor.
<button onClick={() => setCount(count + 1)}> Click me </button>
Antes del lanzamiento de React Hooks, este ejemplo habría usado más líneas de código, ya que tendríamos que usar un componente de class
.
Reglas de los ganchos de reacción
Antes de profundizar en las mejores prácticas, debemos comprender las reglas de React Hooks, que también son algunos de los conceptos fundamentales de las prácticas presentadas en este artículo.
React Hooks son funciones de JavaScript, pero debe seguir dos reglas al usarlas.
- Call Hooks en el nivel superior;
- Solo llama a Hooks desde componentes de React.
Nota : estas dos reglas se introdujeron en React Hooks, en lugar de ser parte del mismo JavaScript.
Veamos estas reglas con más detalle.
Ganchos de llamada en el nivel superior
No llame a Hooks dentro de bucles, condiciones o funciones anidadas. Siempre use Hooks en el nivel superior de su función React. Al seguir esta regla, te aseguras de que los ganchos se llamen en el mismo orden cada vez que se represente un componente. Eso es lo que le permite a React preservar correctamente el estado de los Hooks entre múltiples llamadas useState
y useEffect
.
Hagamos un componente de Form
que tendrá dos estados:
-
accountName
-
accountDetail
Estos estados tendrán valores predeterminados, utilizaremos el useEffect
para conservar el estado en el almacenamiento local de nuestro navegador o en el título de nuestro documento.
Ahora, este componente podrá administrar con éxito su estado si permanece igual entre varias llamadas de useState
y useEffect
.
function Form() { // 1. Use the accountName state variable const [accountName, setAccountName] = useState('David'); // 2. Use an effect for persisting the form useEffect(function persistForm() { localStorage.setItem('formData', accountName); }); // 3. Use the accountDetail state variable const [accountDetail, setAccountDetail] = useState('Active'); // 4. Use an effect for updating the title useEffect(function updateStatus() { document.title = accountName + ' ' + accountDetail; }); // ... }
Si el orden de nuestros Hooks cambia (lo que puede ser posible cuando se llaman en bucles o condicionales), React tendrá dificultades para descubrir cómo preservar el estado de nuestro componente.
// ------------ useState('David') // 1. Initialize the accountName state variable with 'David' useEffect(persistForm) // 2. Add an effect for persisting the form useState('Active') // 3. Initialize the accountdetail state variable with 'Active' useEffect(updateStatus) // 4. Add an effect for updating the status // ------------- // Second render // ------------- useState('David') // 1. Read the accountName state variable (argument is ignored) useEffect(persistForm) // 2. Replace the effect for persisting the form useState('Active') // 3. Read the accountDetail state variable (argument is ignored) useEffect(updateStatus) // 4. Replace the effect for updating the status // ...
Ese es el orden que sigue React para llamar a nuestros ganchos. Dado que el orden sigue siendo el mismo, podrá conservar el estado de nuestro componente. Pero, ¿qué pasa si ponemos una llamada Hook dentro de una condición?
// We're breaking the first rule by using a Hook in a condition if (accountName !== '') { useEffect(function persistForm() { localStorage.setItem('formData', accountName); }); }
La accountName !== ''
es true
en el primer renderizado, así que ejecutamos este Hook. Sin embargo, en el próximo procesamiento, el usuario puede borrar el formulario, haciendo que la condición sea false
. Ahora que omitimos este gancho durante el renderizado, el orden de las llamadas de gancho se vuelve diferente:
useState('David') // 1. Read the accountName state variable (argument is ignored) // useEffect(persistForm) // This Hook was skipped! useState('Active') // 2 (but was 3). Fail to read the accountDetails state variable useEffect(updateStatus) // 3 (but was 4). Fail to replace the effect
React no sabría qué devolver para la segunda llamada useState
Hook. React esperaba que la segunda llamada Hook en este componente correspondiera al efecto persistForm
, al igual que durante el renderizado anterior, pero ya no. A partir de ese momento, cada siguiente llamada de Hook
después de la que omitimos también cambiaría en uno, lo que generaría errores.
Esta es la razón por la cual Hooks debe llamarse en el nivel superior de nuestros componentes. Si queremos ejecutar un efecto condicionalmente, podemos poner esa condición dentro de nuestro Hook.
Nota : consulte los documentos de React Hook para leer más sobre este tema.
Solo ganchos de llamada de componentes React
No llames a Hooks desde funciones regulares de JavaScript. En su lugar, puede llamar a Hooks desde los componentes de la función React. Veamos la diferencia entre la función JavaScript y el componente React a continuación:
Función JavaScript
import { useState } = "react"; function toCelsius(fahrenheit) { const [name, setName] = useState("David"); return (5/9) * (fahrenheit-32); } document.getElementById("demo").innerHTML = toCelsius;
Aquí importamos el useState
del paquete React y luego declaramos nuestra función. Pero esto no es válido ya que no es un componente de React.
Función de reacción
import React, { useState} from "react"; import ReactDOM from "react-dom"; function Account(props) { const [name, setName] = useState("David"); return <p>Hello, {name}! The price is <b>{props.total}</b> and the total amount is <b>{props.amount}</b></p> } ReactDom.render( <Account total={20} amount={5000} />, document.getElementById('root') );
Aunque el cuerpo de ambos parece similar, el último se convierte en un componente cuando importamos React al archivo. Esto es lo que nos permite usar cosas como JSX y React hooks dentro.
Si importó su gancho preferido sin importar React (lo que lo convierte en una función normal), no podrá utilizar el gancho que ha importado ya que solo se puede acceder al gancho en el componente React.
Ganchos de llamadas desde ganchos personalizados
Un Hook personalizado es una función de JavaScript cuyo nombre comienza con use
y que puede llamar a otros Hooks. Por ejemplo, useUserName
se usa debajo de un gancho personalizado que llama a los ganchos useState
y useEffect
. Obtiene datos de una API, recorre los datos y llama a setIsPresent()
si el nombre de usuario específico que recibió está presente en los datos de la API.
export default function useUserName(userName) { const [isPresent, setIsPresent] = useState(false); useEffect(() => { const data = MockedApi.fetchData(); data.then((res) => { res.forEach((e) => { if (e.name === userName) { setIsPresent(true); } }); }); }); return isPresent; }
Luego podemos reutilizar la funcionalidad de este gancho en otros lugares donde lo necesitemos en nuestra aplicación. En tales lugares, excepto cuando sea necesario, ya no tenemos que llamar a useState
o useEffect
.
Al seguir esta regla, se asegura de que toda la lógica con estado en un componente sea claramente visible desde su código fuente.
Complemento ESLint
El complemento ESLint llamado eslint-plugin-react-hooks
aplica las reglas anteriores. Esto es útil para hacer cumplir las reglas cuando se trabaja en un proyecto. Le sugiero que utilice este complemento cuando trabaje en su proyecto, especialmente cuando trabaje con otros. Puede agregar este complemento a su proyecto si desea probarlo:
// Your ESLint configuration { "plugins": [ // ... "react-hooks" ], "rules": { // ... "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks "react-hooks/exhaustive-deps": "warn" // Checks effect dependencies } }
Este complemento se incluye de forma predeterminada en Create React App. Por lo tanto, no necesita agregarlo si inicia sus aplicaciones React usando Create-React-App.
Pensando en anzuelos
Echemos un breve vistazo a los componentes de class
y los componentes funcionales (con Hooks), antes de sumergirnos en las pocas mejores prácticas de Hooks.
La forma más sencilla de definir un componente en React es escribir una función JavaScript que devuelva un elemento React:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
El componente Welcome
acepta props
, que es un objeto que contiene datos y devuelve un elemento React. Luego podemos importar y renderizar este componente en otro componente.
El componente class
utiliza una metodología de programación llamada Encapsulación , lo que básicamente significa que todo lo relevante para el componente de clase vivirá dentro de él. Los métodos de ciclo de vida ( constructors
, componentDidMount()
, render
, etc.) dan a los componentes una estructura predecible.
La encapsulación es uno de los fundamentos de la POO ( Programación orientada a objetos). Se refiere a la agrupación de datos dentro de los métodos que operan con esos datos, y se utiliza para ocultar los valores o el estado de un objeto de datos estructurados dentro de una clase, evitando el acceso directo a ellos por parte de personas no autorizadas.
Con Hooks, la composición de un componente cambia de ser una combinación de Hooks de ciclo de vida a funcionalidades con algún renderizado al final.
Componente de función
El siguiente ejemplo muestra cómo se pueden usar Hooks personalizados en un componente funcional (sin mostrar qué es el cuerpo). Sin embargo, lo que hace o puede hacer no está limitado. Podría ser crear instancias de variables de estado, consumir contextos, suscribir el componente a varios efectos secundarios, ¡o todo lo anterior si está utilizando un gancho personalizado!
function { useHook{...}; useHook{...}; useHook{...}; return (
...); }
Componente de clase
Un componente de class
requiere que se extienda desde React.Component
y cree una función de render
que devuelve un elemento React. Esto requiere más código pero también le dará algunos beneficios.
class { constructor(props) {...} componentDidMount() {...} componentWillUnmount() {...} render() {...} }
Hay algunos beneficios que obtiene al usar componentes funcionales en React:
- Será más fácil separar el contenedor y los componentes de presentación porque necesita pensar más en el estado de su componente si no tiene acceso a
setState()
en su componente. - Los componentes funcionales son mucho más fáciles de leer y probar porque son funciones simples de JavaScript sin enlaces de estado o de ciclo de vida.
- Terminas con menos código.
- El equipo de React mencionó que puede haber un aumento de rendimiento para los componentes funcionales en futuras versiones de React.
Esto lleva a la primera mejor práctica al usar React Hooks.
Mejores prácticas de ganchos
1. Simplifique sus ganchos
Mantener React Hooks simple le dará el poder de controlar y manipular de manera efectiva lo que sucede en un componente a lo largo de su vida útil. Evite escribir Hooks personalizados tanto como sea posible ; puede insertar un useState()
o useEffect()
en lugar de crear su propio enlace.
Si te encuentras haciendo uso de un montón de ganchos personalizados que están relacionados en funcionalidad, puedes crear un gancho personalizado que actúe como un envoltorio para estos. Echemos un vistazo a dos componentes funcionales diferentes con ganchos a continuación.
Componente funcional v1
function { useHook(...); useHook(...); useHook(...); return( <div>...</div> ); }
Componente funcional v2
function { useCustomHook(...); useHook(...); useHook(...); return( <div>...</div> ); }
v2 es una mejor versión porque mantiene el gancho simple y todos los demás useHook
están en línea en consecuencia. Esto nos permite crear funcionalidades que se pueden reutilizar en diferentes componentes y también nos brinda más poder para controlar y manipular nuestros componentes de manera efectiva. En lugar de adoptar la versión 1, en la que nuestros componentes están repletos de Hooks, debe utilizar la versión 2, que facilitará la depuración y limpiará su código.
2. Organiza y estructura tus ganchos
Una de las ventajas de React Hooks es la capacidad de escribir menos código que sea fácil de leer. En algunos casos, la cantidad de useEffect()
y useState()
todavía puede ser confusa. Cuando mantiene su componente organizado, ayudará en la legibilidad y mantendrá el flujo de sus componentes consistente y predecible. Si sus Hooks personalizados son demasiado complicados, siempre puede dividirlos en Hooks subpersonalizados. Extraiga la lógica de su componente a Hooks personalizados para que su código sea legible.
3. Use fragmentos de ganchos de reacción
React Hooks Snippets es una extensión de Visual Studio Code para hacer que React Hooks sea más fácil y rápido. Actualmente, se admiten cinco ganchos:
-
useState()
-
useEffect()
-
useContext()
-
useCallback()
-
useMemo()
También se han añadido otros fragmentos. He intentado trabajar con estos Hooks y ha sido una de las mejores prácticas que he usado personalmente mientras trabajaba con ellos.
Hay dos formas de agregar fragmentos de React Hooks a su proyecto:
- Mando
Inicie VS Code Quick open ( Ctrl + P ), pegueext install ALDuncanson.react-hooks-snippets
y presione Enter . - Mercado de extensiones
Inicie 'VS Code Extension Marketplace' ( Ctrl + Shift + X ) y busque 'React Hook Snippets'. Luego, busque el ícono 'Alduncanson'.
Recomiendo el primer fragmento. Lea más sobre los fragmentos aquí o busque los fragmentos de Hooks más recientes aquí.
4. Tenga en cuenta las reglas de los ganchos
Esfuércese por tener siempre en cuenta las dos reglas de Hooks que aprendimos anteriormente mientras trabajaba con React Hooks.
- Solo llame a sus ganchos en el nivel superior. No llame a Hooks dentro de bucles, condiciones o funciones anidadas.
- Siempre llama a Hooks desde componentes de funciones de React o desde Hooks personalizados, no llames a Hooks desde funciones regulares de JavaScript.
El complemento ESlint llamado eslint-plugin-react-hooks
hace cumplir estas dos reglas, puede agregar este complemento a su proyecto si lo desea, como explicamos anteriormente en la sección de reglas de ganchos.
Las mejores prácticas no se han resuelto por completo porque los Hooks aún son relativamente nuevos. Por lo tanto, la adopción debe tomarse con la precaución que se tomaría al adoptar cualquier tecnología temprana. Con eso en mente, los Hooks son el camino para el futuro de React.
Conclusión
Espero que disfrutes este tutorial. Hemos aprendido las dos reglas más importantes de React Hooks y cómo pensar de manera efectiva en Hooks. Analizamos los componentes funcionales y algunas de las mejores prácticas para escribir Hooks de manera correcta y efectiva. A pesar de lo breves que son las reglas, es importante convertirlas en su brújula guía al escribir reglas. Si es propenso a olvidarlo, puede utilizar el complemento ESLint para aplicarlo.
Espero que tome todas las lecciones aprendidas aquí en su próximo proyecto de React. ¡Buena suerte!
Recursos
- "Presentamos ganchos", React Docs
- "Componentes funcionales frente a clase en React", David Joch, Medium
- “Mixins considerados dañinos”, Dan Abramov, React Blog
- “React Hooks: mejores prácticas y un cambio de mentalidad”, Bryan Manuele, Medium
- "React Hooks Snippets para VS Code", Anthony Davis, Visual Code Marketplace