0

I am learning React Hooks, and I wanna to build a type race game: a mini game to track how quickly you can type out a snippet of text.

The logic is simple: When user starts the game, set the start time, and when the value of the input equals the chosen snippet, display the endTime.

But when I start the game and input the correct words, it doesn't show that I have input the same words.

Open the console, input any words, and I found that react did not capture the last letter I have input, for example, I input abc, the console just show me ab.

So how can I use react hooks to complete the game?

Here is my whole code:

const App = () => {
  const SNIPPETS = [
    'Bears, beets, battlestar galactica',
    "What's Forrest Gump's password? 1Forrest1",
    'Where do programmers like to hangout? The Foo Bar'
  ]

  const INITIAL_GAME_STATE = { victory: false, startTime: null, endTime: null }

  const [snippet, setSnippet] = useState('')
  const [gameState, setGameState] = useState(INITIAL_GAME_STATE)
  const [userText, setUserText] = useState('')

  useEffect(() => {
    console.log('change')
    if (gameState.victory) {
      document.title = gameState.victory ? 'Victory!' : 'Playing...'
    }
  }, [gameState.victory])

  const updateUserText = e => {
    setUserText(e.target.value)
    console.log(snippet)
    console.log(userText)
    if (userText === snippet) {
      console.log('same')
      setGameState({
        ...gameState,
        victory: true,
        endTime: new Date().getTime() - gameState.startTime
      })
    }
  }

  const chooseSnippet = snippetIndex => () => {
    setSnippet(SNIPPETS[snippetIndex])
    setGameState({
      ...gameState,
      startTime: new Date().getTime()
    })
  } 

  return (
    <div className="game-wrapper">
      <h2>Type Race</h2>
      { snippet && (
        <>
          <h4>{ snippet }</h4>
          <input type="textarea" value={ userText } onChange={ updateUserText } />
          <br />
        </>
      ) }
      <br/>
      <button onClick={ chooseSnippet(0) }>Start a new race!</button>
      <br/>
      { gameState.victory && (
        <h4>`Done! <span></span>Time: ${ gameState.endTime }ms`</h4>
      ) }
      <br/>
      {
        SNIPPETS.map((SNIPPET, index) => (
          <button onClick={ chooseSnippet(index) } key={ index }>
            { SNIPPET.substring(0, 10) }...
          </button>
        ))
      }
    </div>
  )
}
norbitrial
  • 14,716
  • 7
  • 32
  • 59
Liam_1998
  • 1,157
  • 3
  • 12
  • 27

2 Answers2

3

Because React setState is asynchronous, so your userText value will not be updated after you call setUserText. To get an updated userText, wait for the next rendering of the component.

useEffect(() => {
  if (userText === snippet) {
    // do something ...    
  }
}, [userText]);

For more information: useState set method not reflecting change immediately

Trí Phan
  • 1,123
  • 2
  • 15
  • 33
0

just as Trí Phan answered, setState is asynchronous in hooks too. so I fixed it:

useEffect(() => {
    if (userText === snippet && userText) {
      setGameState({
        ...gameState,
        victory: true,
        endTime: new Date().getTime() - gameState.startTime
      })
    }
  }, [userText])
Liam_1998
  • 1,157
  • 3
  • 12
  • 27