fix : infinite rerenders

This commit is contained in:
GuillaumeSD
2024-02-25 03:02:59 +01:00
parent 7412f708d0
commit 2af25cf4ec
11 changed files with 50 additions and 47 deletions

View File

@@ -1,8 +1,8 @@
# FreeChess # FreeChess
This project is solely a frontend with tools for the chess community. Freechess is an open-source chess GUI to store, view and analyze your chess games.
It is built with [Next.js](https://nextjs.org/docs), [React](https://react.dev/learn/describing-the-ui) and [TypeScript](https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html). It is built with [Next.js](https://nextjs.org/docs), [React](https://react.dev/learn/describing-the-ui), [Material UI](https://mui.com/material-ui/getting-started/overview/), and [TypeScript](https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html).
It is deployed on [Firebase](https://firebase.google.com/docs/hosting), you can see it live [here](https://freechess.web.app). It is deployed on [Firebase](https://firebase.google.com/docs/hosting), you can see it live [here](https://freechess.web.app).

View File

@@ -7,7 +7,7 @@
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint && tsc --noEmit", "lint": "next lint && tsc --noEmit",
"deploy": "firebase deploy --project=freechess --only hosting" "deploy": "firebase deploy --project=freechessproject --only hosting"
}, },
"dependencies": { "dependencies": {
"@emotion/react": "^11.11.1", "@emotion/react": "^11.11.1",

View File

@@ -1,42 +1,45 @@
import { Chess, Move } from "chess.js"; import { Chess, Move } from "chess.js";
import { PrimitiveAtom, useAtom } from "jotai"; import { PrimitiveAtom, useAtom } from "jotai";
import { useCallback } from "react";
export const useChessActions = (chessAtom: PrimitiveAtom<Chess>) => { export const useChessActions = (chessAtom: PrimitiveAtom<Chess>) => {
const [game, setGame] = useAtom(chessAtom); const [game, setGame] = useAtom(chessAtom);
const setPgn = (pgn: string) => { const setPgn = useCallback(
const newGame = new Chess(); (pgn: string) => {
newGame.loadPgn(pgn); const newGame = new Chess();
setGame(newGame); newGame.loadPgn(pgn);
}; setGame(newGame);
},
[setGame]
);
const reset = () => { const reset = useCallback(() => {
setGame(new Chess()); setGame(new Chess());
}; }, [setGame]);
const copyGame = () => { const copyGame = useCallback(() => {
const newGame = new Chess(); const newGame = new Chess();
newGame.loadPgn(game.pgn()); newGame.loadPgn(game.pgn());
return newGame; return newGame;
}; }, [game]);
const move = (move: { const makeMove = useCallback(
from: string; (move: { from: string; to: string; promotion?: string }): Move | null => {
to: string; const newGame = copyGame();
promotion?: string; const result = newGame.move(move);
}): Move | null => { setGame(newGame);
const newGame = copyGame();
const result = newGame.move(move);
setGame(newGame);
return result; return result;
}; },
[copyGame, setGame]
);
const undo = () => { const undoMove = useCallback(() => {
const newGame = copyGame(); const newGame = copyGame();
newGame.undo(); newGame.undo();
setGame(newGame); setGame(newGame);
}; }, [copyGame, setGame]);
return { setPgn, reset, move, undo }; return { setPgn, reset, makeMove, undoMove };
}; };

View File

@@ -18,8 +18,8 @@ import { useRouter } from "next/router";
import { useEffect } from "react"; import { useEffect } from "react";
export default function GameReport() { export default function GameReport() {
const boardActions = useChessActions(boardAtom); const { reset: resetBoard } = useChessActions(boardAtom);
const gameActions = useChessActions(gameAtom); const { setPgn: setGamePgn } = useChessActions(gameAtom);
const setEval = useSetAtom(gameEvalAtom); const setEval = useSetAtom(gameEvalAtom);
const setBoardOrientation = useSetAtom(boardOrientationAtom); const setBoardOrientation = useSetAtom(boardOrientationAtom);
const router = useRouter(); const router = useRouter();
@@ -27,12 +27,12 @@ export default function GameReport() {
useEffect(() => { useEffect(() => {
if (!gameId) { if (!gameId) {
boardActions.reset(); resetBoard();
setEval(undefined); setEval(undefined);
setBoardOrientation(true); setBoardOrientation(true);
gameActions.setPgn(new Chess().pgn()); setGamePgn(new Chess().pgn());
} }
}, [gameId, boardActions, gameActions, setEval, setBoardOrientation]); }, [gameId, setEval, setBoardOrientation, resetBoard, setGamePgn]);
return ( return (
<Grid <Grid

View File

@@ -21,7 +21,7 @@ export default function EvaluationBar({ height }: Props) {
useEffect(() => { useEffect(() => {
const bestLine = currentMove?.eval?.lines[0]; const bestLine = currentMove?.eval?.lines[0];
if (!bestLine) return; if (!bestLine || bestLine.depth < 6) return;
const evalBar = getEvaluationBarValue(bestLine, isWhiteToPlay); const evalBar = getEvaluationBarValue(bestLine, isWhiteToPlay);
setEvalBar(evalBar); setEvalBar(evalBar);

View File

@@ -20,12 +20,12 @@ export default function Board() {
const boardOrientation = useAtomValue(boardOrientationAtom); const boardOrientation = useAtomValue(boardOrientationAtom);
const showBestMoveArrow = useAtomValue(showBestMoveArrowAtom); const showBestMoveArrow = useAtomValue(showBestMoveArrowAtom);
const showPlayerMoveArrow = useAtomValue(showPlayerMoveArrowAtom); const showPlayerMoveArrow = useAtomValue(showPlayerMoveArrowAtom);
const boardActions = useChessActions(boardAtom); const { makeMove: makeBoardMove } = useChessActions(boardAtom);
const currentMove = useAtomValue(currentMoveAtom); const currentMove = useAtomValue(currentMoveAtom);
const onPieceDrop = (source: Square, target: Square): boolean => { const onPieceDrop = (source: Square, target: Square): boolean => {
try { try {
const result = boardActions.move({ const result = makeBoardMove({
from: source, from: source,
to: target, to: target,
promotion: "q", // TODO: Let the user choose the promotion promotion: "q", // TODO: Let the user choose the promotion

View File

@@ -40,9 +40,8 @@ export default function AnalyzePanel() {
engineDepth, engineDepth,
engineMultiPv engineMultiPv
); );
console.log(newGameEval);
setEval(newGameEval);
setEval(newGameEval);
setEvaluationInProgress(false); setEvaluationInProgress(false);
if (gameFromUrl) { if (gameFromUrl) {

View File

@@ -16,20 +16,20 @@ import { useRouter } from "next/router";
export default function LoadGame() { export default function LoadGame() {
const router = useRouter(); const router = useRouter();
const game = useAtomValue(gameAtom); const game = useAtomValue(gameAtom);
const gameActions = useChessActions(gameAtom); const { setPgn: setGamePgn } = useChessActions(gameAtom);
const boardActions = useChessActions(boardAtom); const { reset: resetBoard } = useChessActions(boardAtom);
const { gameFromUrl } = useGameDatabase(); const { gameFromUrl } = useGameDatabase();
const setEval = useSetAtom(gameEvalAtom); const setEval = useSetAtom(gameEvalAtom);
const setBoardOrientation = useSetAtom(boardOrientationAtom); const setBoardOrientation = useSetAtom(boardOrientationAtom);
const resetAndSetGamePgn = useCallback( const resetAndSetGamePgn = useCallback(
(pgn: string) => { (pgn: string) => {
boardActions.reset(); resetBoard();
setEval(undefined); setEval(undefined);
setBoardOrientation(true); setBoardOrientation(true);
gameActions.setPgn(pgn); setGamePgn(pgn);
}, },
[boardActions, gameActions, setEval, setBoardOrientation] [resetBoard, setGamePgn, setEval, setBoardOrientation]
); );
useEffect(() => { useEffect(() => {

View File

@@ -5,7 +5,7 @@ import { boardAtom, gameAtom } from "../states";
import { useChessActions } from "@/hooks/useChess"; import { useChessActions } from "@/hooks/useChess";
export default function GoToLastPositionButton() { export default function GoToLastPositionButton() {
const boardActions = useChessActions(boardAtom); const { setPgn: setBoardPgn } = useChessActions(boardAtom);
const game = useAtomValue(gameAtom); const game = useAtomValue(gameAtom);
const board = useAtomValue(boardAtom); const board = useAtomValue(boardAtom);
@@ -20,7 +20,7 @@ export default function GoToLastPositionButton() {
<IconButton <IconButton
onClick={() => { onClick={() => {
if (isButtonDisabled) return; if (isButtonDisabled) return;
boardActions.setPgn(game.pgn()); setBoardPgn(game.pgn());
}} }}
disabled={isButtonDisabled} disabled={isButtonDisabled}
> >

View File

@@ -10,7 +10,8 @@ import SaveButton from "./saveButton";
export default function ReviewPanelToolBar() { export default function ReviewPanelToolBar() {
const board = useAtomValue(boardAtom); const board = useAtomValue(boardAtom);
const boardActions = useChessActions(boardAtom); const { reset: resetBoard, undoMove: undoBoardMove } =
useChessActions(boardAtom);
const boardHistory = board.history(); const boardHistory = board.history();
@@ -21,7 +22,7 @@ export default function ReviewPanelToolBar() {
<Tooltip title="Reset board"> <Tooltip title="Reset board">
<Grid> <Grid>
<IconButton <IconButton
onClick={() => boardActions.reset()} onClick={() => resetBoard()}
disabled={boardHistory.length === 0} disabled={boardHistory.length === 0}
> >
<Icon icon="ri:skip-back-line" /> <Icon icon="ri:skip-back-line" />
@@ -32,7 +33,7 @@ export default function ReviewPanelToolBar() {
<Tooltip title="Go to previous move"> <Tooltip title="Go to previous move">
<Grid> <Grid>
<IconButton <IconButton
onClick={() => boardActions.undo()} onClick={() => undoBoardMove()}
disabled={boardHistory.length === 0} disabled={boardHistory.length === 0}
> >
<Icon icon="ri:arrow-left-s-line" height={30} /> <Icon icon="ri:arrow-left-s-line" height={30} />

View File

@@ -5,7 +5,7 @@ import { boardAtom, gameAtom } from "../states";
import { useChessActions } from "@/hooks/useChess"; import { useChessActions } from "@/hooks/useChess";
export default function NextMoveButton() { export default function NextMoveButton() {
const boardActions = useChessActions(boardAtom); const { makeMove: makeBoardMove } = useChessActions(boardAtom);
const game = useAtomValue(gameAtom); const game = useAtomValue(gameAtom);
const board = useAtomValue(boardAtom); const board = useAtomValue(boardAtom);
@@ -23,7 +23,7 @@ export default function NextMoveButton() {
const nextMove = game.history({ verbose: true })[nextMoveIndex]; const nextMove = game.history({ verbose: true })[nextMoveIndex];
if (nextMove) { if (nextMove) {
boardActions.move({ makeBoardMove({
from: nextMove.from, from: nextMove.from,
to: nextMove.to, to: nextMove.to,
promotion: nextMove.promotion, promotion: nextMove.promotion,