Search code examples
javascriptreactjstypescriptsettimeout2d-games

Reveal left over underscores one at a time at end of Hangman TypeScript game


When the game is over, I am trying to replace the underscores with the letters they missed one at a time using a setTimeout. This function swaps the underscore with the correct letter but, on the following render puts the letter back to the underscore and repeats this process until the end where it only displays the last missing letter. I need all the letters to fully swap out the state.underscores, which is an array of strings.

state.missingIndex is an array of numbers; missing letter indexes

word is the complete word the player is guessing

using useReducer hook to change state

React hooks and Typescript

  const displayMissingLetters = () => {
    let wrongWord: string[] = [];

    state.losses.missingIndex.forEach((num, index) => {
      setTimeout(() => {
        wrongWord = state.underscores
          .map((alpha: string, i: number) =>
            alpha === "_" && word[i] === word[num] ? word[num] : alpha
          );

        dispatch({ type: "SET_UNDERSCORES", underscores: wrongWord });

      }, index * 300);
    });
  }

This is what it looks like as of right now:

revealing underscores

This is what wrongWord looks like in the console:

console underscores


Solution

  • The problem is that when the timeout function fires, it has a stale copy of the state, set when all the timeouts are first scheduled.

    It looks like you're using reducers here, so the easiest thing would be to handle this in a reducer. Reducers get the current state as they begin to run, so they shouldn't ever have old/stale state.

    setTimeout(() => {
      dispatch({ type: "REVEAL_LETTER", index: num });
    }, index * 300)
    

    Then move the logic that replaces an underscore with a letter, and saves that back in the state, to the reducer.