fix(memory-quiz): prevent input lag during rapid typing in room mode
When typing rapidly in room mode, users had to type each digit 8+ times before it registered. This was caused by reading stale state.currentInput values during rapid keypresses before React could re-render with the optimistically updated state. Solution: Use a ref to track the current input value and update it immediately when keys are pressed, before waiting for the network round-trip and React re-render. Changes: - Add currentInputRef to track input value immediately - Update ref in useEffect to stay in sync with state - Use ref instead of state.currentInput in keyboard handlers - Clear ref immediately when accepting/rejecting numbers This fixes the async network validation issue where local state updates were too slow for rapid user input. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,15 @@ export function InputPhase() {
|
||||
'neutral'
|
||||
)
|
||||
|
||||
// Use a ref to track the current input to avoid stale reads during rapid typing
|
||||
// This prevents the "type 8 times" issue where state.currentInput hasn't updated yet
|
||||
const currentInputRef = useRef(state.currentInput)
|
||||
|
||||
// Keep ref in sync with state
|
||||
useEffect(() => {
|
||||
currentInputRef.current = state.currentInput
|
||||
}, [state.currentInput])
|
||||
|
||||
// Use keyboard state from parent state instead of local state
|
||||
const { hasPhysicalKeyboard, testingMode, showOnScreenKeyboard } = state
|
||||
|
||||
@@ -107,6 +116,7 @@ export function InputPhase() {
|
||||
const acceptCorrectNumber = useCallback(
|
||||
(number: number) => {
|
||||
acceptNumber?.(number)
|
||||
currentInputRef.current = '' // Clear ref immediately
|
||||
setInput?.('')
|
||||
setDisplayFeedback('correct')
|
||||
|
||||
@@ -121,7 +131,7 @@ export function InputPhase() {
|
||||
)
|
||||
|
||||
const handleIncorrectGuess = useCallback(() => {
|
||||
const wrongNumber = parseInt(state.currentInput, 10)
|
||||
const wrongNumber = parseInt(currentInputRef.current, 10)
|
||||
if (!Number.isNaN(wrongNumber)) {
|
||||
dispatch({ type: 'ADD_WRONG_GUESS_ANIMATION', number: wrongNumber })
|
||||
// Clear wrong guess animations after explosion
|
||||
@@ -131,6 +141,7 @@ export function InputPhase() {
|
||||
}
|
||||
|
||||
rejectNumber?.()
|
||||
currentInputRef.current = '' // Clear ref immediately
|
||||
setInput?.('')
|
||||
setDisplayFeedback('incorrect')
|
||||
|
||||
@@ -140,7 +151,7 @@ export function InputPhase() {
|
||||
if (state.guessesRemaining - 1 === 0) {
|
||||
setTimeout(() => showResults?.(), 1000)
|
||||
}
|
||||
}, [dispatch, rejectNumber, setInput, showResults, state.guessesRemaining, state.currentInput])
|
||||
}, [dispatch, rejectNumber, setInput, showResults, state.guessesRemaining])
|
||||
|
||||
// Simple keyboard event handlers that will be defined after callbacks
|
||||
const handleKeyboardInput = useCallback(
|
||||
@@ -150,7 +161,9 @@ export function InputPhase() {
|
||||
// Only handle if input phase is active and guesses remain
|
||||
if (state.guessesRemaining === 0) return
|
||||
|
||||
const newInput = state.currentInput + key
|
||||
// Use ref for immediate value, update ref right away to prevent stale reads
|
||||
const newInput = currentInputRef.current + key
|
||||
currentInputRef.current = newInput // Update ref immediately for next keypress
|
||||
setInput?.(newInput)
|
||||
|
||||
// Clear any existing timeout
|
||||
@@ -202,8 +215,9 @@ export function InputPhase() {
|
||||
)
|
||||
|
||||
const handleKeyboardBackspace = useCallback(() => {
|
||||
if (state.currentInput.length > 0) {
|
||||
const newInput = state.currentInput.slice(0, -1)
|
||||
if (currentInputRef.current.length > 0) {
|
||||
const newInput = currentInputRef.current.slice(0, -1)
|
||||
currentInputRef.current = newInput // Update ref immediately
|
||||
setInput?.(newInput)
|
||||
|
||||
// Clear any existing timeout
|
||||
@@ -214,7 +228,7 @@ export function InputPhase() {
|
||||
|
||||
setDisplayFeedback('neutral')
|
||||
}
|
||||
}, [state.currentInput, state.prefixAcceptanceTimeout, dispatch, setInput])
|
||||
}, [state.prefixAcceptanceTimeout, dispatch, setInput])
|
||||
|
||||
// Set up global keyboard listeners
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user