Escribí un programa que suma o resta una unidad para count presionando las teclas arriba y abajo en el teclado. El problema es que el programa no funciona correctamente. Cuando presiones la tecla, mira la consola, que se ejecuta varias veces, mientras que cada vez que presionas la tecla, solo tiene que ejecutarse una vez. Este es un problema que falla después de presionar los botones arriba o abajo varias veces, por favor ayuda
import React, { useState } from "react"; export default function Example() { const [count, setCount] = useState(0); document.addEventListener("keydown", function (e) { switch (e.keyCode) { case 38: setCount(count + 1); console.log(count); break; case 40: setCount(count - 1); console.log(count); break; } }); return ( <div> <p>You clicked {count} times</p> </div> ); }Está agregando un controlador de eventos cada vez que se representa el componente. El componente se vuelve a renderizar cada vez que cambia el estado. Es un bucle exponencial, por lo que su aplicación "falla".
useEffect para adjuntar el detector de eventos una vez que el componente esté montado y elimínelo nuevamente una vez que el componente esté desmontado.setCount para acceder al recuento actual. export default function Example() { const [count, setCount] = useState(0); useEffect(() => { const keyDownCallback = function (e) { switch (e.keyCode) { case 38: setCount((count) => count + 1); break; case 40: setCount((count) => count - 1); break; } }; document.addEventListener("keydown", keyDownCallback); return () => document.removeEventListener("keydown", keyDownCallback); }, []); return ( <div> <p>You clicked {count} times</p> </div> ); }Documentación
Bueno, cada vez que su componente se vuelve a renderizar, adjunta ese detector de eventos una y otra vez y llama a setCount demasiadas veces y vuelve a renderizar el componente demasiadas veces. Por lo tanto, su programa cae en un bucle infinito. Creo que deberías probar los ganchos useEffect y useLayoutEffect.
Editar
import React from "react"; export default function App() { const [count, setCount] = React.useState(0); const handleUpdateCount = React.useCallback( (value) => setCount((prevCount) => prevCount + value), [setCount] ); React.useEffect(() => { const keyDownCallback = (e) => { switch (e.keyCode) { case 38: handleUpdateCount(1); break; case 40: handleUpdateCount(-1); break; default: console.log("default"); } }; document.addEventListener("keydown", keyDownCallback); return () => document.removeEventListener("keydown", keyDownCallback); }, [handleUpdateCount]); return ( <div> <p>You clicked {count} times</p> </div> ); }De esta manera, prevenimos re-renderizaciones adicionales.
Y sobre useLayoutEffect, este enlace se ejecuta con una devolución de llamada después de todas las mutaciones DOM, pero de acuerdo con la documentación oficial de React, deberíamos tender a usar useEffect como sea posible. https://reactjs.org/docs/hooks-reference.html
Debe colocar document.addEventListener en un useEffect o agregará un detector de eventos cada vez que se vuelva a procesar el componente
useEffect(() => { document.addEventListener("keydown", function (e) { switch (e.keyCode) { case 38: setCount(count + 1); console.log(count); break; case 40: setCount(count - 1); console.log(count); break; } }); }, [])