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:
+17
-16
@@ -4,8 +4,8 @@ import "@vimazing/typing-chud/game.css";
|
||||
import { useKeyBindings } from "./useKeyBindings";
|
||||
|
||||
function App() {
|
||||
const [gameMode, setGameMode] = useState<GameMode>("words");
|
||||
const gameManager = useGame({ gameMode }, useKeyBindings);
|
||||
const [gameMode, setGameMode] = useState<GameMode>("letters");
|
||||
const gameManager = useGame({ initialGameMode: gameMode }, useKeyBindings);
|
||||
const { containerRef, gameStatus, scoreManager } = gameManager;
|
||||
|
||||
const formatTime = (ms: number) => {
|
||||
@@ -21,20 +21,21 @@ function App() {
|
||||
<h1 className="text-2xl font-bold text-center">Typing Chud</h1>
|
||||
<div className="flex gap-2 justify-center text-sm">
|
||||
{(['letters', 'words', 'sentences', 'paragraphs'] as const).map((mode) => (
|
||||
<button
|
||||
key={mode}
|
||||
onClick={() => {
|
||||
setGameMode(mode);
|
||||
gameManager.quitGame();
|
||||
}}
|
||||
className={`px-3 py-1 rounded capitalize font-medium transition ${
|
||||
gameMode === mode
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'bg-muted text-foreground hover:bg-blue-500 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
{mode}
|
||||
</button>
|
||||
<button
|
||||
key={mode}
|
||||
onClick={(e) => {
|
||||
setGameMode(mode);
|
||||
gameManager.changeGameMode(mode);
|
||||
e.currentTarget.blur(); // Remove focus from button
|
||||
}}
|
||||
className={`px-3 py-1 rounded capitalize font-medium transition ${
|
||||
gameMode === mode
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'bg-muted text-foreground hover:bg-blue-500 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
{mode}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,44 +2,44 @@ import { useEffect } from "react";
|
||||
import type { GameManager } from "../../src";
|
||||
|
||||
export const useKeyBindings = (gameManager: GameManager) => {
|
||||
const { gameStatus, renderBoard, startGame, quitGame, clearKeyLog, handleTypedLetter } = gameManager;
|
||||
|
||||
useEffect(() => {
|
||||
const handler = (ev: KeyboardEvent) => {
|
||||
// start game
|
||||
if (ev.code === "Space") {
|
||||
if (["waiting", "game-over", "game-won"].includes(gameStatus)) {
|
||||
clearKeyLog();
|
||||
renderBoard();
|
||||
if (gameStatus === "game-over" || gameStatus === "game-won") {
|
||||
quitGame();
|
||||
setTimeout(() => startGame(), 0);
|
||||
ev.preventDefault(); // Prevent button activation
|
||||
if (["waiting", "game-over", "game-won"].includes(gameManager.gameStatus)) {
|
||||
gameManager.clearKeyLog();
|
||||
gameManager.renderBoard();
|
||||
if (gameManager.gameStatus === "game-over" || gameManager.gameStatus === "game-won") {
|
||||
gameManager.quitGame();
|
||||
setTimeout(() => gameManager.startGame(), 0);
|
||||
} else {
|
||||
startGame();
|
||||
gameManager.startGame();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ignore other keys while waiting
|
||||
if (gameStatus === "waiting") return;
|
||||
if (gameManager.gameStatus === "waiting") return;
|
||||
|
||||
// quit
|
||||
if (ev.key === "Escape") {
|
||||
quitGame();
|
||||
ev.preventDefault();
|
||||
gameManager.quitGame();
|
||||
return;
|
||||
}
|
||||
|
||||
// handle letter typing during game
|
||||
if (gameStatus === "started") {
|
||||
if (gameManager.gameStatus === "started") {
|
||||
if (ev.key.length === 1 && /[a-zA-Z]/.test(ev.key)) {
|
||||
handleTypedLetter(ev.key);
|
||||
gameManager.handleTypedLetter(ev.key);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", handler);
|
||||
return () => window.removeEventListener("keydown", handler);
|
||||
}, [gameStatus, handleTypedLetter]);
|
||||
}, [gameManager.gameStatus]);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user