Context value not updating inside event listener in React

I am building a simple game in react. The problem is context is properly updated but the context value inside an event listener function is not updating. If I access the value outside the event function then the new updated value is rendered.

For the first keyup event the value will be 0 but for the next events, it should be a new updated value.

  const updateGame = useUpdateGame();
  const gameData = useGameData(); 
  //Assume Default gameData.board value is 0

  // Assume context value updated to 5

  useEffect(() => {
    document.addEventListener("keyup", (e) => {
      e.stopImmediatePropagation();
      console.log(gameData.board) //0
      handleKeyPress(e.key, gameData.board, updateGame);
    });
  }, []);
  console.log(gameData.board) //5

>Solution :

The event listener has closed over the old value of context (think closures). You will need to keep the updated value of the context in the event listener.

This could either be done by defining the event listener everytime the context value changes or using a ref.

  1. Register and clear the event listener in the useEffect after every render when gameData changes.
const updateGame = useUpdateGame();
  const gameData = useGameData(); 

  useEffect(() => {
    let listener = (e) => {
      e.stopImmediatePropagation();
      handleKeyPress(e.key, gameData.board, updateGame);
    };

    document.addEventListener("keyup", listener);

return () => {
    document.removeEventListener("keyup", listener);
};
  }, [gameData]); //updateGame should be added here too ideally
  1. Keep a ref which mimics the value you are looking for. ref are stable containers so they will always have the correct value of the state
  const updateGame = useUpdateGame();
  const gameData = useGameData(); 
  const gameDataRef = useRef(gameData?.board ?? null);


  useEffect(() => {
    document.addEventListener("keyup", (e) => {
      e.stopImmediatePropagation();
      handleKeyPress(e.key, gameDataRef.current, updateGame);
    });
  }, []);

  useEffect(() =>{
   gameDataRef.current = gameData.board;
  },[gameData]);

Leave a Reply