fix : infinite rerenders
This commit is contained in:
@@ -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).
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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(
|
||||||
|
(pgn: string) => {
|
||||||
const newGame = new Chess();
|
const newGame = new Chess();
|
||||||
newGame.loadPgn(pgn);
|
newGame.loadPgn(pgn);
|
||||||
setGame(newGame);
|
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;
|
|
||||||
promotion?: string;
|
|
||||||
}): Move | null => {
|
|
||||||
const newGame = copyGame();
|
const newGame = copyGame();
|
||||||
const result = newGame.move(move);
|
const result = newGame.move(move);
|
||||||
setGame(newGame);
|
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 };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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(() => {
|
||||||
|
|||||||
@@ -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}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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} />
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user