Skip to content

Commit 4b841c7

Browse files
committed
fix : board perf optimization
1 parent cc9a45a commit 4b841c7

2 files changed

Lines changed: 151 additions & 115 deletions

File tree

src/components/board/index.tsx

Lines changed: 135 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
Square,
1111
} from "react-chessboard/dist/chessboard/types";
1212
import { useChessActions } from "@/hooks/useChessActions";
13-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
13+
import { useCallback, useMemo, useRef, useState } from "react";
1414
import { Color, MoveClassification } from "@/types/enums";
1515
import { Chess } from "chess.js";
1616
import { getSquareRenderer } from "./squareRenderer";
@@ -64,10 +64,11 @@ export default function Board({
6464
const boardHue = useAtomValue(boardHueAtom);
6565

6666
const gameFen = game.fen();
67-
68-
useEffect(() => {
67+
const [previousFen, setPreviousFen] = useState(gameFen);
68+
if (gameFen !== previousFen) {
69+
setPreviousFen(gameFen);
6970
setClickedSquares([]);
70-
}, [gameFen, setClickedSquares]);
71+
}
7172

7273
const isPiecePlayable = useCallback(
7374
({ piece }: { piece: string }): boolean => {
@@ -78,117 +79,134 @@ export default function Board({
7879
[canPlay, game]
7980
);
8081

81-
const onPieceDrop = (
82-
source: Square,
83-
target: Square,
84-
piece: string
85-
): boolean => {
86-
if (!isPiecePlayable({ piece })) return false;
87-
88-
const result = playMove({
89-
from: source,
90-
to: target,
91-
promotion: piece[1]?.toLowerCase() ?? "q",
92-
});
93-
94-
return !!result;
95-
};
96-
97-
const resetMoveClick = (square?: Square | null) => {
98-
setMoveClickFrom(square ?? null);
99-
setMoveClickTo(null);
100-
setShowPromotionDialog(false);
101-
if (square) {
102-
const moves = game.moves({ square, verbose: true });
103-
setPlayableSquares(moves.map((m) => m.to));
104-
} else {
105-
setPlayableSquares([]);
106-
}
107-
};
108-
109-
const handleSquareLeftClick = (square: Square, piece?: string) => {
110-
setClickedSquares([]);
111-
112-
if (!moveClickFrom) {
113-
if (piece && !isPiecePlayable({ piece })) return;
114-
resetMoveClick(square);
115-
return;
116-
}
117-
118-
const validMoves = game.moves({ square: moveClickFrom, verbose: true });
119-
const move = validMoves.find((m) => m.to === square);
82+
const onPieceDrop = useCallback(
83+
(source: Square, target: Square, piece: string): boolean => {
84+
if (!isPiecePlayable({ piece })) return false;
12085

121-
if (!move) {
122-
resetMoveClick(square);
123-
return;
124-
}
86+
const result = playMove({
87+
from: source,
88+
to: target,
89+
promotion: piece[1]?.toLowerCase() ?? "q",
90+
});
12591

126-
setMoveClickTo(square);
92+
return !!result;
93+
},
94+
[isPiecePlayable, playMove]
95+
);
12796

128-
if (
129-
move.piece === "p" &&
130-
((move.color === "w" && square[1] === "8") ||
131-
(move.color === "b" && square[1] === "1"))
132-
) {
133-
setShowPromotionDialog(true);
134-
return;
135-
}
97+
const resetMoveClick = useCallback(
98+
(square?: Square | null) => {
99+
setMoveClickFrom(square ?? null);
100+
setMoveClickTo(null);
101+
setShowPromotionDialog(false);
102+
if (square) {
103+
const moves = game.moves({ square, verbose: true });
104+
setPlayableSquares(moves.map((m) => m.to));
105+
} else {
106+
setPlayableSquares([]);
107+
}
108+
},
109+
[setMoveClickFrom, setMoveClickTo, setPlayableSquares, game]
110+
);
136111

137-
const result = playMove({
138-
from: moveClickFrom,
139-
to: square,
140-
});
112+
const handleSquareLeftClick = useCallback(
113+
(square: Square, piece?: string) => {
114+
setClickedSquares([]);
141115

142-
resetMoveClick(result ? undefined : square);
143-
};
116+
if (!moveClickFrom) {
117+
if (piece && !isPiecePlayable({ piece })) return;
118+
resetMoveClick(square);
119+
return;
120+
}
144121

145-
const handleSquareRightClick = (square: Square) => {
146-
setClickedSquares((prev) =>
147-
prev.includes(square)
148-
? prev.filter((s) => s !== square)
149-
: [...prev, square]
150-
);
151-
};
122+
const validMoves = game.moves({ square: moveClickFrom, verbose: true });
123+
const move = validMoves.find((m) => m.to === square);
152124

153-
const handlePieceDragBegin = (_: string, square: Square) => {
154-
resetMoveClick(square);
155-
};
125+
if (!move) {
126+
resetMoveClick(square);
127+
return;
128+
}
156129

157-
const handlePieceDragEnd = () => {
158-
resetMoveClick();
159-
};
130+
setMoveClickTo(square);
160131

161-
const onPromotionPieceSelect = (
162-
piece?: PromotionPieceOption,
163-
from?: Square,
164-
to?: Square
165-
) => {
166-
if (!piece) return false;
167-
const promotionPiece = piece[1]?.toLowerCase() ?? "q";
132+
if (
133+
move.piece === "p" &&
134+
((move.color === "w" && square[1] === "8") ||
135+
(move.color === "b" && square[1] === "1"))
136+
) {
137+
setShowPromotionDialog(true);
138+
return;
139+
}
168140

169-
if (moveClickFrom && moveClickTo) {
170141
const result = playMove({
171142
from: moveClickFrom,
172-
to: moveClickTo,
173-
promotion: promotionPiece,
143+
to: square,
174144
});
175-
resetMoveClick();
176-
return !!result;
177-
}
178145

179-
if (from && to) {
180-
const result = playMove({
181-
from,
182-
to,
183-
promotion: promotionPiece,
184-
});
185-
resetMoveClick();
186-
return !!result;
187-
}
146+
resetMoveClick(result ? undefined : square);
147+
},
148+
[
149+
game,
150+
isPiecePlayable,
151+
moveClickFrom,
152+
playMove,
153+
resetMoveClick,
154+
setClickedSquares,
155+
]
156+
);
188157

189-
resetMoveClick(moveClickFrom);
190-
return false;
191-
};
158+
const handleSquareRightClick = useCallback(
159+
(square: Square) => {
160+
setClickedSquares((prev) =>
161+
prev.includes(square)
162+
? prev.filter((s) => s !== square)
163+
: [...prev, square]
164+
);
165+
},
166+
[setClickedSquares]
167+
);
168+
169+
const handlePieceDragBegin = useCallback(
170+
(_: string, square: Square) => {
171+
resetMoveClick(square);
172+
},
173+
[resetMoveClick]
174+
);
175+
176+
const handlePieceDragEnd = useCallback(() => {
177+
resetMoveClick();
178+
}, [resetMoveClick]);
179+
180+
const onPromotionPieceSelect = useCallback(
181+
(piece?: PromotionPieceOption, from?: Square, to?: Square) => {
182+
if (!piece) return false;
183+
const promotionPiece = piece[1]?.toLowerCase() ?? "q";
184+
185+
if (moveClickFrom && moveClickTo) {
186+
const result = playMove({
187+
from: moveClickFrom,
188+
to: moveClickTo,
189+
promotion: promotionPiece,
190+
});
191+
resetMoveClick();
192+
return !!result;
193+
}
194+
195+
if (from && to) {
196+
const result = playMove({
197+
from,
198+
to,
199+
promotion: promotionPiece,
200+
});
201+
resetMoveClick();
202+
return !!result;
203+
}
204+
205+
resetMoveClick(moveClickFrom);
206+
return false;
207+
},
208+
[moveClickFrom, moveClickTo, playMove, resetMoveClick]
209+
);
192210

193211
const customArrows: Arrow[] = useMemo(() => {
194212
const bestMove = position?.lastEval?.bestMove;
@@ -249,6 +267,22 @@ export default function Board({
249267
[pieceSet]
250268
);
251269

270+
const customBoardStyle = useMemo(() => {
271+
const commonBoardStyle = {
272+
borderRadius: "5px",
273+
boxShadow: "0 2px 10px rgba(0, 0, 0, 0.5)",
274+
};
275+
276+
if (boardHue) {
277+
return {
278+
...commonBoardStyle,
279+
filter: `hue-rotate(${boardHue}deg)`,
280+
};
281+
}
282+
283+
return commonBoardStyle;
284+
}, [boardHue]);
285+
252286
return (
253287
<Grid
254288
container
@@ -293,11 +327,7 @@ export default function Board({
293327
boardOrientation={
294328
boardOrientation === Color.White ? "white" : "black"
295329
}
296-
customBoardStyle={{
297-
borderRadius: "5px",
298-
boxShadow: "0 2px 10px rgba(0, 0, 0, 0.5)",
299-
filter: `hue-rotate(${boardHue}deg)`,
300-
}}
330+
customBoardStyle={customBoardStyle}
301331
customArrows={customArrows}
302332
isDraggablePiece={isPiecePlayable}
303333
customSquare={SquareRenderer}

src/components/board/squareRenderer.tsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { CurrentPosition } from "@/types/eval";
22
import { MoveClassification } from "@/types/enums";
33
import { PrimitiveAtom, atom, useAtomValue } from "jotai";
44
import Image from "next/image";
5-
import { CSSProperties, forwardRef } from "react";
5+
import { CSSProperties, forwardRef, useMemo } from "react";
66
import {
77
CustomSquareProps,
88
Square,
@@ -36,23 +36,29 @@ export function getSquareRenderer({
3636
const toSquare = position.lastMove?.to;
3737
const moveClassification = position?.eval?.moveClassification;
3838

39-
const highlightSquareStyle: CSSProperties | undefined =
40-
clickedSquares.includes(square)
41-
? rightClickSquareStyle
42-
: fromSquare === square || toSquare === square
43-
? previousMoveSquareStyle(moveClassification)
44-
: undefined;
39+
const highlightSquareStyle: CSSProperties | undefined = useMemo(
40+
() =>
41+
clickedSquares.includes(square)
42+
? rightClickSquareStyle
43+
: fromSquare === square || toSquare === square
44+
? previousMoveSquareStyle(moveClassification)
45+
: undefined,
46+
[clickedSquares, square, fromSquare, toSquare, moveClassification]
47+
);
4548

46-
const playableSquareStyle: CSSProperties | undefined =
47-
playableSquares.includes(square) ? playableSquareStyles : undefined;
49+
const playableSquareStyle: CSSProperties | undefined = useMemo(
50+
() =>
51+
playableSquares.includes(square) ? playableSquareStyles : undefined,
52+
[playableSquares, square]
53+
);
4854

4955
return (
5056
<div
5157
ref={ref}
5258
style={{
5359
...style,
5460
position: "relative",
55-
filter: `hue-rotate(-${boardHue}deg)`,
61+
filter: boardHue ? `hue-rotate(-${boardHue}deg)` : undefined,
5662
}}
5763
>
5864
{children}

0 commit comments

Comments
 (0)