Una mirada más profunda a los ganchos React más populares
Pawel Rybczynski
Software Engineer
En el transcurso de muchas entrevistas, me he dado cuenta de que incluso los programadores experimentados tienen problemas para distinguir los Hooks, por no hablar de sus capacidades más avanzadas. Por ello, intentaré explicar en este artículo cómo deben utilizarse los Hooks.
Lo más importante que debes recordar sobre Hooks:
sólo pueden utilizarse en componentes de función - los componentes de clase tienen su propia implementación del ciclo de vida;
siempre empiezan con utilice;
puedes utilizar tantos Hooks como quieras, pero debes recordar que su uso tiene un impacto en el rendimiento general;
deben ejecutarse en el mismo orden en cada renderizado... pero ¿por qué? Veamos un ejemplo:
<code>srcFunctionComponent.js
Línea 11:5: React El hook "useEffect" es llamado condicionalmente. <strong>React Ganchos</strong> deben llamarse exactamente en el mismo orden en cada componente render .eslintreact-hooks/rules-of-hooks
Como puedes ver, es sólo una advertencia de eslint, así que puedes desactivarla añadiendo un comando desde abajo en la parte superior del archivo ComponenteFunción
/* eslint-disable react-hooks/rules-of-hooks */
y funcionará pero sólo hasta que cumplamos la condición que ejecuta nuestro Hook. Lo siguiente que veremos es este error.
Error no detectado: Renderizados más ganchos que durante el renderizado anterior.
React 5
FunctionComponent FunctionComponent.js:11
React 12
unstable_runWithPriority planificador.desarrollo.js:468
React 17
js index.js:7
js main.chunk.js:905
Webpack 7
react-dom.development.js:15162
¿Por qué ocurre esto? React se basa en el orden en que se llaman los Hooks ya que React no sabría qué devolver para el useEffect porque no hubiera tal Hook en la línea para comprobar.
Recuerde, eslint es una herramienta poderosa, nos ayuda a detectar muchos errores potenciales. Desactivar sus advertencias es algo peligroso, comprueba siempre si ignorar la advertencia puede causar un fallo en la aplicación.
useState
Seguro que ya sabes cómo queda 😉
const [valor, setValue] = useState(0);
Por lo tanto, tiene 4 elementos: estado (valor reactivo), función de actualización (setter), gancho real (función) y valor inicial opcional. ¿Por qué devuelve un array? Porque podemos reestructurarlo como queramos.
Ahora quiero centrarme en el último elemento: el valor inicial. Hay dos maneras de pasar el estado inicial:
Por valor codificado o algo - que será llamado en cada renderizado
const [valor, setValue] = useState(0);
Por una versión de la función. Es realmente útil si queremos ejecutar el estado inicial sólo una vez, en el primer render. ¿Quizás necesitas hacer muchos cálculos complejos para recibir el estado inicial? Disminuirá el coste de recursos, ¡yupi!
Otra cosa que puede crear errores en el flujo de la aplicación: probablemente sepas cómo actualizar un estado, ¿verdad?
setValue(1);
Bien... pero ¿y si quiero actualizar el estado basándome en un estado anterior?
setValue(valor + 1);
Sí... Pero no... ¿Y si intentas llamar a la función setter dos veces, una detrás de otra? La forma recomendada de actualizar un estado basándose en el estado anterior es utilizar una función. Garantiza que te estás refiriendo al estado anterior
Este Hook toma 2 argumentos (el segundo es opcional) y lo usamos para manejar efectos secundarios. Y dependiendo de lo que pasemos como segundo argumento, el Hook será llamado de forma diferente:
sin segundo argumento - cada render
useEffect(() => {
doSomething();
});
array vacío - sólo en la primera renderización
useEffect(() => {
doSomething();
}, []);
con dependencias - cada vez que cambia el valor de la matriz de dependencias
useEffect(() => {
hacerAlgo(valor);
}, [valor]);
Limpieza
Con useEffect, podemos utilizar algo que se llama limpieza. ¿Para qué sirve? Es muy útil, pero creo que es mejor para limpiar escuchadores de eventos. Digamos que quieres crear un escuchador de eventos que depende de algún estado. No quieres añadir un nuevo escuchador de eventos en cada cambio de estado, porque después de unos cuantos renders habrá tantos escuchadores que afectará al rendimiento de la aplicación. Una gran manera de evitar estas cosas es utilizar la función de limpieza. ¿Cómo hacerlo? Simplemente añade una función de retorno al useEffect.
Debido a que está dentro del hook useEffect, el retorno es llamado dependiendo del array de dependencias - en cada render, sólo en el primer render, o cuando el valor en el array de dependencias cambia. Pero cuando el componente es desmontado, la limpieza será llamada en el segundo argumento sin importar qué. El retorno código se llama antes de que el código real de Hook. Es muy lógico - al principio limpiar el viejo, a continuación, crear uno nuevo. ¿Verdad?
Al principio, parece correcto. Estás obteniendo algunos datos, y cuando los datos llegan, actualizas el estado. Y aquí está la trampa:
A veces recibirá una advertencia de este tipo: No se puede realizar una actualización de estado React en un componente desmontado. Esto es un no-op, pero indica una fuga de memoria en su aplicación. Para solucionarlo, cancele todas las suscripciones y tareas asíncronas en una función de limpieza useEffect.
La razón es que el componente puede ser desmontado mientras tanto, pero la aplicación seguirá intentando actualizar el estado de ese componente después de que la promesa se haya cumplido. ¿Cómo solucionarlo? Es necesario comprobar si el componente existe.
Nota: Hay un Hook muy similar => useLayoutEffect() - el callback se ejecuta después de renderizar el componente pero antes de que el dom se actualice visualmente. Es útil cuando se trabaja con getBoundingClientRect(), pero deberías usar useEffect por defecto. ¿Por qué? Porque puede bloquear las actualizaciones visuales - cuando tienes un código complejo dentro de tu effect Hook.
useContext
¿Para qué sirve? Compartir datos sin pasar props. Consta de los siguientes elementos:
Contexto creado - datos
Proveedor de contexto: proporciona contexto a todos los niños
En child, necesitas importar el contexto y llamar al hook useContext y pasar ese contexto como argumento.
import { UserContext } from "./App";
const { nombre } = useContext(UserContext);
devolver <h1>Hola {nombre}<>
```
Voilà. Tiene buena pinta. Principalmente para pasar datos globales como temas, etc. No recomendado para usar en tareas con cambios muy dinámicos.
Por supuesto, podemos crear un proveedor de contexto personalizado y un Hook personalizado para reducir el boilerplate en su lugar... pero me ocuparé de los Hooks personalizados en el próximo artículo.
useReducer
Nos permite gestionar el estado y volver a renderizar cuando el estado cambia - como useState. Es similar al reductor redux. Este es mejor que useState cuando la lógica del estado es más complicada.
¿Para qué sirve? Se puede utilizar cuando queremos restablecer el estado a su valor inicial. Abajo puedes encontrar un bonito ejemplo:
// Padre
// Hijo
function init(númeroinicial) {
return { número: númeroinicial };
}
function reducer(state, action) {
switch (acción.tipo) {
case "cambio":
return { number: Math.random() };
case "reset":
return init(action.payload);
por defecto
throw new Error();
}
}
export default function ComponenteInfantil({ getFactorial }) {
const [state, dispatch] = useReducer(reducer, initialNumber, init);
return (
<>
<h2>Número: {state.number}</h2>
<button
onclick="{()" > dispatch({ type: "reset", payload: initialNumber })}
>
Reiniciar
</button>
<button onclick="{()" > dispatch({ type: "change" })}>Dibujar</button>
</>
);
}
Número: {state.number}
useCallback
¿Cuándo utilizarlo? Cuando queremos conseguir igualdad referencial (reduciendo así el número de funciones creadas). Este Hook devuelve la función, a diferencia de useMemo que devuelve el valor.
Ejemplo: Crear una función en el componente padre y luego pasarla a través de props
Se registrará en la consola en cada representación. Incluso si los valores dentro de la getSquaredValue() no ha cambiado. Pero podemos evitar esto envolviendo esa función en useCallback
No es neutral cuando se miran los costes de los recursos - useMemo debe ser llamado en cada render, guarda el valor en memoria y lo compara (sobrecarga de memoria),
utiliza Memoization - la técnica de optimización, forma específica de almacenamiento en caché.
Deberías usarlo sólo en 2 escenarios:
Si quieres evitar llamar a un código complejo en cada render;
Si desea lograr la igualdad referencial.
Veamos un poco más de cerca el segundo caso. Queremos usar useEffect con un objeto como dependencia. Como los objetos se comparan por su referencia, useEffect será llamado en cada render. Para evitar esto, podemos combinar useEffect con useMemo para memoizar dichos objetos y luego pasar esos objetos memoizados al array de dependencias. Breve ejemplo:
El objeto 'hobbit' hace que las dependencias del gancho useEffect (linea 49) cambien en cada render. Muévelo dentro del callback useEffect. Alternativamente, envuelva la inicialización de 'hobbit' en su propio gancho useMemo () Hook.eslintreact-hooks/exhaustive-deps
Lo más importante: useRef no dispara el re-renderizado (como useState) porque no está conectado al ciclo de renderizado - mantiene la misma referencia entre renders.
const ref = useRef(0);
Para llamar al valor guardado, es necesario utilizar una propiedad actual (ref es un objeto) - ref.actual
El segundo caso para el que podemos utilizar ese Gancho es para referenciar elementos dentro de HTML. Cada elemento tiene un atributo ref. Así, podemos manejar focus, eventos, etc.
El tercer caso es que podemos utilizar refs para manejar componentes no controlados. Puede leer más sobre ellos en documentos react, pero en resumen, se parece a esto:
Como puedes ver, no hay manejador de eventos, sólo recuerda el valor tecleado. Es genial para manejar formularios básicos cuando sólo quieres leer los valores guardados cuando los necesitas (como al enviar).
Bonus: Es genial cuando necesitas recordar valores de estado anteriores. Puede utilizar para ello el gancho useEffect, sólo tiene que pasar el estado a la ref.
Como puedes ver, los ganchos no son tan obvios. Podemos combinarlos para resolver muchos problemas. Seguro que te beneficiarás mucho estudiando este tema.
Y también hay ganchos personalizados...
En conclusión, Ganchos React han revolucionado la forma de Desarrolladores React edificio de aproximación aplicaciones web . Al proporcionar una forma más intuitiva y eficaz de gestionar el estado y el ciclo de vida de los componentes funcionales, los hooks se han convertido en parte integrante de React desarrollo .
Tanto si eres un desarrollador experimentado como si acabas de empezar con React, entender los hooks más populares y sus casos de uso es crucial. Con hooks como useState, useEffect, useContext, y más, Componentes React puede construirse con un código más limpio y reutilizable. Además, la capacidad de crear ganchos personalizados permite a los desarrolladores encapsular y compartir la lógica a través de múltiples componentes, promoviendo la reutilización del código y la modularidad. A medida que React siga evolucionando e introduciendo nuevas funciones, los hooks desempeñarán sin duda un papel fundamental para aprovechar todo el potencial del framework.
Así que, tanto si trabaja en una pequeña aplicación funcional como en una aplicación web a gran escala, adoptar Ganchos React mejorará su flujo de trabajo de desarrollo y desbloqueará un sinfín de posibilidades para crear aplicaciones sólidas y con multitud de funciones Aplicaciones React .