paint-brush
Por qué es importante el orden en los Hooks de Reactpor@itsrennyman
4,536 lecturas
4,536 lecturas

Por qué es importante el orden en los Hooks de React

por Renato Pozzi2022/06/20
Read on Terminal Reader
Read this story w/o Javascript

Demasiado Largo; Para Leer

Los React Hooks son una nueva característica en React 16.8. Le permiten usar el estado y otras características de React sin escribir una clase. Son una forma poderosa de escribir componentes con estado y son una excelente manera de escribir componentes funcionales. Sin embargo, todo este poder tiene un costo. Tienen algunas restricciones que debe seguir para que funcionen bien, de lo contrario, terminará con muchos errores.

Coin Mentioned

Mention Thumbnail
featured image - Por qué es importante el orden en los Hooks de React
Renato Pozzi HackerNoon profile picture


Los React Hooks son una nueva característica en React 16.8. Le permiten usar el estado y otras características de React sin escribir una clase. Son una forma poderosa de escribir componentes con estado y son una excelente manera de escribir componentes funcionales.


Sin embargo, todo este poder tiene un costo. Tienen algunas restricciones que debe seguir para que funcionen bien, de lo contrario, terminará con muchos errores.


Hoy quiero hablar de una regla específica:


No llame a Hooks dentro de bucles, condiciones o funciones anidadas.


Entonces, simplemente no podemos hacer algo como esto:


 import * as React from "react"; const Iron = ({ isMelted = false }) => { if (isMelted) { const [temperature, setTemperature] = React.useState(null); } return <div>{...}</div>; };


O peor aún algo como:


 <button onClick={() => useRequest({ id: 12 })}> {n + 1} </button>


A veces, las personas que leen esta regla la aplican sin hacer demasiadas preguntas sobre por qué y cómo, y si estás entre ellos, está bien, no hay vergüenza en seguir los documentos sin profundizar, pero el destino quiere que estés aquí para eso mismo. razón, por lo que te pregunto: ¿podrías decirme por qué es tan importante?


Antes de cualquier explicación, quiero que encienda su herramienta de resolución de problemas llamada cerebro y le daré cinco minutos para encontrar una solución, luego puede desplazarse por el artículo para obtener información.


¿Cómo estuvo tu sesión de resolución de problemas? ¡Espero que hayas encontrado algo realmente genial! Sumerjámonos en la luz, implementando nuestro propio estado de uso .


La aplicación inicial será esta, ¿adivinen qué? Otro contador… Pero te servirá para comparar la solución personalizada con la real.


 import ReactDOM from "react-dom"; import { useState } from "react"; // The actual Component export default function App() { const [counter, setCounter] = useState(10); const increment = () => setCounter(counter + 1); return ( <div> <button onClick={increment}>{counter}</button> </div> ); } ReactDOM.render(<App />, document.getElementById("root"));


Vamos a usar React 17 debido a que la nueva lógica de renderizado en la versión 18 no funciona muy bien con una solución “casera”, pero ya sabes, es solo un experimento 😇

Nuestro estado de uso personalizado

Nuestro objetivo es llamar a useState personalizado en lugar del real, desmitifiquemos el comportamiento de este gancho:

  • Puede aceptar un parámetro con un valor inicial para el estado.
  • Devuelve una tupla con el valor real y una función para actualizar ese valor.
  • Una vez que se actualiza el estado, se activa una nueva representación del componente manteniendo el valor actualizado.


Entonces, lo primero que haremos es declarar nuestra función con algunos marcadores de posición básicos y comentar sobre la función real 💅


 // import { useState } from "react"; function useState(initialValue) { const setValue = (newValue) => {}; const tuple = [initialValue, setValue]; return tuple; }


Genial, ahora nada falla, pero tampoco funciona... nuestra función setValue no hace nada. Necesitamos darle funcionalidad real, pero es posible que note un problema aquí: ¿cómo se almacena el estado en la función?


Quiero decir, todos saben que los componentes React son solo funciones, ¿verdad? Y React mismo llama a estas funciones que activan la representación de los componentes, pero para cada nueva invocación de los componentes de la App inicializamos una nueva función useState.


 App(); // A new useState is invoked App(); // A new useState is invoked App(); // A new useState is invoked


Entonces, para resolver este problema, necesitamos una variable externa que se usará como almacén para nuestra declaración de ganchos. Llamémoslo estado .


 // This variable will be persistent between renders! let state = []; function useState(initialValue) { const setValue = (newValue) => {}; const tuple = [initialValue, setValue]; return tuple; }


Ahora es el momento de implementar la lógica central del enlace, una versión inicial podría ser algo como esto:


 let state = null; function useState(initialValue) { if (state && state[0]) { return state; } const setValue = (newValue) => { state[0] = newValue; customRender(); // Who am I? }; state = [initialValue, setValue]; return state; }


Analicemos el comportamiento: en la llamada inicial, useState verificará si en el índice específico de la matriz de estados ya hay algo, si es así, lo devolverá, de lo contrario, completará la variable de estado con la tupla y la devolverá.


 // First Render: Initialize with the Tuple // Second Render: State is not null, so returns it. // Third Render: State is not null. so returns it. // Continue Infinitely...


Pero WTH Renato, tuve la urgencia de probar este código, y nada funciona. ¿¡¿Me estás tomando el pelo?!?


Mire cuidadosamente el fragmento de código anterior, ¿vio la invocación de la función customRender ? Bueno, este es nuestro extraño truco para simular un renderizado en reaccionar. Simplemente creamos una función que envuelve la invocación de ReactDOM.render() y la llamamos cuando establecemos el nuevo valor.


 // Wrap the render function into a function. function customRender() { ReactDOM.render(<App />, document.getElementById("root")); } // Don't forget to call it immediately, we need our initial render :) customRender();


Si pruebas este código, notarás que en realidad funciona como el real, aquí te dejo el sandbox.



¡Genial, ahora es el momento de hacer que todo explote!


Mira este nuevo sandbox que pongo aquí:



¿Puedes detectar el error? Eso no es genial... cada botón tiene el mismo valor de estado 🥲 ¡tal vez sea hora de una mejor implementación!

¡Es hora de una mejor implementación!

El primer problema obvio es que nuestra variable de estado acepta un solo valor, por lo que debe convertirse en una matriz; además, necesitamos una forma de realizar un seguimiento del índice de nuestras llamadas useState , porque, para cada estado, habrá diferentes valores. !


¡Aquí puede encontrar una versión de trabajo con los dos botones diferentes que finalmente disfrutan de sus propios valores!


La respuesta a nuestra pregunta

Hasta ahora nos hemos preguntado por qué es importante el orden en los ganchos, y espero que ahora descubras la respuesta por ti mismo.


La razón es simplemente esta variable:


 const states = []; // I'm a bad Guy 😙


Aunque fue una implementación muy ingenua, internamente reaccionar funciona de manera similar a esto. Cada definición de gancho se almacena con un índice específico, por lo que React se basa en él para devolver el valor correcto.


Como vimos en el primer ejemplo, esa es la razón por la cual hacer esto no es correcto:


 import * as React from "react"; const Iron = ({ isMelted = false }) => { // Sometimes the index can be zero, sometimes not? // There is no consistency between renders! if (isMelted) { const [temperature, setTemperature] = React.useState(null); } return <div></div>; };


También puede encontrar útil esta respuesta de las preguntas frecuentes de React:


¿Cómo asocia React las llamadas Hook con los componentes?


Hay una lista interna de "celdas de memoria" asociadas con cada componente. Son solo objetos de JavaScript donde podemos poner algunos datos. Cuando llamas a un gancho como useState(), lee la celda actual (o la inicializa durante el primer procesamiento) y luego mueve el puntero a la siguiente . Así es como varias llamadas a useState() obtienen cada una un estado local independiente.


También publicado aquí