Cada vez que trabajo con addEventListener() , y también quiero acceder a algún estado dentro de useEffect , tengo el mismo problema. No puedo agregar el estado como dependencia, porque entonces crearía múltiples detectores de eventos cada vez que cambia el estado. Casi siempre me encuentro atrapado con la advertencia " React Hook useEffect has a missing dependency ". Digamos que tengo un componente que necesita cambiar su estado en window.onClick() y en window.onDoubleClick() . Si el estado es verdadero, hacer clic debería cambiarlo a falso, y si el estado es falso, hacer doble clic debería cambiarlo a verdadero. Así que esto es lo que debería escribir:
import React, { useState, useEffect } from 'react'; export default function someComponent() { const [toggle, setToggle] = useState(false); useEffect(() => { window.addEventListener('click', (event) => { if (toggle) setToggle(false) }) window.addEventListener('dblclick', (event) => { if (!toggle) setToggle(true) }) }, []) return ( <p>The toggle state is {toggle.toString()}</p> ); } Este código funciona, pero recibo la advertencia de dependencia faltante. No puedo agregar alternar a la matriz de dependencia, porque luego agregará otro detector de eventos cada vez que toggle el estado de alternancia.
¿Qué estoy haciendo mal aquí? ¿Cómo debo arreglar esto?
Editar: tal vez este ejemplo no fue demasiado bueno, pero es lo más simple que se me ocurrió. Pero, este problema también es para cuando creo otros detectores de eventos, que tienen que estar en el objeto de Windows, como el desplazamiento. Sé que puedo usar return para eliminar el detector de eventos cada vez, pero para eventos como el desplazamiento, lo hace mucho más lento. No tiene sentido para mí tener que eliminarlo y agregarlo cada vez, cuando simplemente no necesito que se dispare nuevamente.
Con reaccionar no tienes que usar el elemento ventana en este caso. Ni siquiera un efecto de uso.
Al usar el gancho useEffect, le está diciendo a reaccionar que haga algo después del renderizado (dependiendo de la matriz de dependencia). En este caso, no es necesario cambiar el estado inmediatamente después de renderizar la página, solo cuando el usuario interactúa con el elemento.
Agregar eventos de clic a través de useEffect probablemente no sea necesario la mayor parte del tiempo y hacerlo como el ejemplo a continuación probablemente le ahorrará tiempo y un dolor de cabeza y tal vez incluso rendimiento (corríjame si me equivoco).
Yo personalmente lo haría así.
import React, { useState } from 'react'; export default function someComponent() { const [toggle, setToggle] = useState(false); return ( <p onClick={() => setToggle(false)} onDoubleClick={() => setToggle(true)} > The toggle state is {toggle.toString()} </p> ); }También podrías llamar funciones desde el elemento así
const [toggle, setToggle] = useState(false); const handleClick = () => { if (toggle) { setToggle(false); } }; const handleDoubleClick = () => { if (!toggle) { setToggle(true); } }; return ( <p onClick={() => handleClick()} onDoubleClick={() => handleDoubleClick()} > The toggle state is {toggle.toString()} </p> );Ejemplo de CodeSandbox
Puede agregar una función de limpieza al enlace useEffect para eliminar los oyentes antiguos. De esta manera, puede pasar la alternancia a la matriz de dependencias y no tendrá oyentes de eventos apilados.
https://reactjs.org/docs/hooks-effect.html
useEffect(() => { const handleClick = () => toggle ? setToggle(false) : setToggle(true); window.addEventListener('click', handleClick); window.addEventListener('dblclick', handleClick); return () => { window.removeEventListener('click', handleClick); window.removeEventListener('dblclick', handleClick); } }, [toggle]);No puedo agregar el estado como dependencia, porque entonces crearía múltiples detectores de eventos cada vez que cambia el estado.
Hay una forma de evitar esto, y esdevolver una función de limpieza desde la devolución de llamada useEffect. Lo animo a leer la sección vinculada de los documentos, luego la solución a continuación se volverá mucho más clara:
useEffect(() => { const handleClick = () => { setToggle(!toggle) } window.addEventListener('click', handleClick) return () => { window.removeEventListener('click', handleClick) } }, [toggle]) con la solución anterior, cada vez que se actualiza la toggle , se llama a la función de limpieza, que elimina el detector de eventos actual antes de volver a ejecutar el efecto.
También tenga en cuenta que puede proporcionar una función de devolución de llamada a setToggle , que recibe el valor actual de alternar y devuelve el nuevo valor. Con este enfoque, no necesitaría pasar toggle como una dependencia para usarEffect:
useEffect(() => { const handleClick = () => { setToggle(currentValue => !currentValue) } window.addEventListener("click", handleClick) return () => { window.removeEventListener("click", handleClick) } }, [])