'Why does render triggered only once every two keydowns?
Problem :
Goal : Each time I press on keys "W+C+N", an image appears randomly on my screen.
Actual result : The image appears only every two keydowns. For example, if I press 4 times on "W+C+N", only 2 new images appear.
What I've tried / guessing where the problem stems from :
Misuse of my useEffect, it either comes from :
- a miscomprehension of the removeEventListener method, does it "disables" my setcoordinates inside of my handleKey function?
- not specifying the right dependency [handleKey] : handleKey function is activated by keypress and changes on new coordinates. So it seems correct to re-render each time i pass a new coordinate
- VS Code warning : "the 'handleKey" function makes the dependencies of useState Hook change on every render. Move it inside the useEffect callback." I did try that but it doesn't work anymore then.
My actual code
import egg from "./logo.png";
import { useState, useEffect } from "react";
function App() {
let map = []; //array of keys to store
const [coordinates, setcoordinates] = useState([]); //array of image coordinates
const handleKey = (event) => {
onkeydown = onkeyup = function (event) {
map[event.keyCode] = event.type === "keydown";
};
//random image coordinates
if (map[87] && map[67] && map[78]) {
const newCoordinates = [...coordinates];
let random_left = Math.floor(Math.random() * window.innerWidth);
let random_top = Math.floor(Math.random() * window.innerHeight);
newCoordinates.push({ left: random_left, top: random_top });
setcoordinates(newCoordinates);
}
};
useEffect(() => {
window.addEventListener("keypress", handleKey);
return () => {
window.removeEventListener("keypress", handleKey);
};
}, [handleKey]);
return (
<div className="App">
<img src={egg} alt="o easter egg" />
<h1>Rocambole</h1>
<h2>Good luck!</h2>
{console.log(coordinates)}
{coordinates.map((item, index) => {
return (
<img
key={index}
src={egg}
alt="egg"
className="newImg"
style={{ top: `${item.top}px`, left: `${item.left}px` }}
/>
);
})}
</div>
);
}
export default App;
Solution 1:[1]
Try updating the state with a function, this way you ensure that the event listener registred in memory gets the fresh state every time, and also move the function handleKey inside useEffect:
import egg from "./logo.png";
import { useState, useEffect } from "react";
function App() {
let map = []; //array of keys to store
const [coordinates, setcoordinates] = useState([]); //array of image coordinates
useEffect(() => {
const handleKey = (event) => {
onkeydown = onkeyup = function (event) {
map[event.keyCode] = event.type === "keydown";
};
//random image coordinates
if (map[87] && map[67] && map[78]) {
let random_left = Math.floor(Math.random() * window.innerWidth);
let random_top = Math.floor(Math.random() * window.innerHeight);
setcoordinates(coordinates =>[...coordinates, { left: random_left, top: random_top }]);
}
};
window.addEventListener("keypress", handleKey);
return () => {
window.removeEventListener("keypress", handleKey);
};
}, []);
return (
<div className="App">
<img src={egg} alt="o easter egg" />
<h1>Rocambole</h1>
<h2>Good luck!</h2>
{console.log(coordinates)}
{coordinates.map((item, index) => {
return (
<img
key={index}
src={egg}
alt="egg"
className="newImg"
style={{ top: `${item.top}px`, left: `${item.left}px` }}
/>
);
})}
</div>
);
}
export default App;
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
