I am trying to initialize useState hook with a class object then trying to update it based on a click eventListner but it is not updating more than one time.
function App() {
useEffect(() => {
const func = (e) => {
console.log('click')
handleKeyPress(10, 0)
}
document.addEventListener('click', func)
return () => document.removeEventListener('click', func)
}, [])
console.log('BEGIN')
const [player, setPlayer] = useState({
position: { x: 100, y: 100 },
direction: 'right',
})
console.log(player.position)
const handleKeyPress = (xChange, yChange) => {
const newX = player.position.x xChange
const newY = player.position.y yChange
let newDirection = player.direction
if (xChange > 0) {
newDirection = 'right'
}
if (xChange < 0) {
newDirection = 'left'
}
console.log('------->', player.position)
setPlayer((prev) => ({
...prev,
direction: newDirection,
position: { x: newX, y: newY },
}))
}
return (
<GameContainer className="game-container">
<Character positionx={player.position.x} positiony={player.position.y} />
</GameContainer>
)
}
export default App
console log can be seen below ->
as you can see the results it is not updating the x value of player state with every click also note that the string "BEGIN" is printing repeatedly with each click that means I think the render function is reinitiallizing the useState again and again.
CodePudding user response:
useEffect(() => {
const func = (e) => {
console.log('click')
handleKeyPress(10, 0)
}
document.addEventListener('click', func)
return () => document.removeEventListener('click', func)
}, [])
The empty dependency array at the end of this use effect means you only want to run this effect once, when the component first mounts. So you will be using the version of handleKeyPress
which existed at that time.
const newX = player.position.x xChange
const newY = player.position.y yChange
In handleKeyPress
, you're using the player
variable to calculate your new values. Since this is the handleKeyPress
from the first render, it's also the player
from the first render, so position.x and position.y are always 100.
There are two ways you can fix this. First, you can list your dependencies in your useEffect, so that it gets updated every time those dependencies change. In your case, that would be a dependency array of [handleKeyPress]
, which will actually change on every single render, so at that point you might as well leave out the dependency array entirely. You could also move handleKeyPress inside of the useEffect, and then change the dependency array to [player]
.
The second option is to make changes to handleKeyPress so it always uses the latest state. You're already using the function version of setPlayer
to get the latest state, but you need to use it for all your calculations:
const handleKeyPress = (xChange, yChange) => {
setPlayer((prev) => {
const newX = prev.position.x xChange;
const newY = prev.position.y yChange;
let newDirection = prev.direction;
if (xChange > 0) {
newDirection = "right";
}
if (xChange < 0) {
newDirection = "left";
}
return {
...prev,
direction: newDirection,
position: { x: newX, y: newY },
};
});
};