fix: prevent button focus from interfering with game controls when changing modes

- Add blur() to mode buttons to remove focus after clicking
- Add preventDefault() to space/escape handlers to stop button activation
- Fix stale closures in useKeyBindings by accessing gameManager directly
- Remove all useCallback hooks per React 19 best practices
- Remove function dependencies from useEffect hooks
This commit is contained in:
2025-11-04 00:09:24 +00:00
parent 308eafac62
commit 2795931d56
6 changed files with 77 additions and 66 deletions
+14 -5
View File
@@ -1,5 +1,5 @@
import { useState, useEffect } from "react";
import type { GameManager, GameOptions, KeyLogEntry, FallingLetter } from "../types";
import type { GameManager, GameOptions, KeyLogEntry, FallingLetter, GameMode } from "../types";
import { useBoard } from "../useBoard";
import { useCursor } from "../useCursor";
import { useGameStatus } from "../useGameStatus";
@@ -10,11 +10,11 @@ export function useGame(
platformHook?: unknown
): GameManager {
const [keyLog, setKeyLog] = useState<KeyLogEntry[]>([]);
const gameMode = options?.gameMode ?? 'letters';
const [gameMode, setGameModeState] = useState<GameMode>(options?.initialGameMode ?? 'letters');
const boardManager = useBoard();
const cursorManager = useCursor();
const gameStatusManager = useGameStatus();
const gameStatusManager = useGameStatus(undefined, gameMode);
// Wrap renderBoard to include player position
const renderBoardWrapped = (letters: any = []) => {
@@ -38,12 +38,20 @@ export function useGame(
// Update gameMode when it changes (without recreating gameStatus state)
useEffect(() => {
gameStatusManager.setGameMode(gameMode);
}, [gameMode, gameStatusManager]);
}, [gameMode]);
const changeGameMode = (mode: GameMode) => {
setGameModeState(mode);
// Only quit if the game is actively running
if (gameStatusManager.gameStatus === 'started') {
gameStatusManager.quitGame();
}
};
// Set the game loop callback
useEffect(() => {
gameStatusManager.setGameLoopCallback(handleGameLoopTick);
}, [gameStatusManager, cursorManager, boardManager]);
}, []);
// Initial render on mount
useEffect(() => {
@@ -65,6 +73,7 @@ export function useGame(
clearKeyLog: () => setKeyLog([]),
getKeyLog: () => [...keyLog],
handleTypedLetter: gameStatusManager.handleTypedLetter,
changeGameMode,
};
if (typeof platformHook === 'function') {