Home > Software design >  React useState not updating with setState function
React useState not updating with setState function

Time:12-20

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 ->

console log

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 },
    };
  });
};
  • Related