fixed stale state closure bug by using useRef() in ChessGameContextProvider
This commit is contained in:
+1
-1
@@ -79,7 +79,7 @@ function socketIOServerInit(server) {
|
||||
// io.to(roomID).emit("new user joined the room");
|
||||
console.log(data, "joined");
|
||||
let room = getRoom(roomID);
|
||||
io.to(roomID).emit(USER_JOINED_ROOM, data.username);
|
||||
socket.to(roomID).emit(USER_JOINED_ROOM, data.username);
|
||||
socket.emit(result, room.gameHistory); // room joined successfully
|
||||
} else {
|
||||
socket.emit(result); // room is full
|
||||
|
||||
@@ -12,7 +12,6 @@ const { GAME_END, CHESS_MOVE } = SOCKET_EVENTS;
|
||||
export const ChessGameContext = createContext();
|
||||
|
||||
const reducer = (state, action) => {
|
||||
// console.log('Before', state);
|
||||
try {
|
||||
switch (action.type) {
|
||||
case SELECT_PIECE:
|
||||
@@ -78,12 +77,9 @@ const reducer = (state, action) => {
|
||||
return state;
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Error', err);
|
||||
console.log('After', state);
|
||||
// state.chess.getBoard()
|
||||
console.error(err);
|
||||
return state;
|
||||
}
|
||||
// console.log('After', state);
|
||||
return state;
|
||||
}
|
||||
|
||||
function chessGameStateInit(myColor) {
|
||||
@@ -107,6 +103,16 @@ const ChessGameContextProvider = ({ children }) => {
|
||||
const [{ chess, chessBoard, moveHints, selected, gameHistory, currentIndex, hasGameEnded, gameEndedReason }, dispatch] = useReducer(reducer, myColor, chessGameStateInit);
|
||||
const [isTimerOn, setIsTimerOn] = useState(true);
|
||||
|
||||
const chessRef = useRef(chess);
|
||||
const moveHintsRef = useRef(moveHints);
|
||||
const selectedRef = useRef(selected);
|
||||
const gameHistoryRef = useRef(gameHistory);
|
||||
const currentIndexRef = useRef(currentIndex);
|
||||
chessRef.current = chess;
|
||||
selectedRef.current = selected;
|
||||
moveHintsRef.current = moveHints;
|
||||
gameHistoryRef.current = gameHistory;
|
||||
currentIndexRef.current = currentIndex;
|
||||
|
||||
const moveAudioRef = useRef(null);
|
||||
const captureAudioRef = useRef(null);
|
||||
@@ -116,38 +122,35 @@ const ChessGameContextProvider = ({ children }) => {
|
||||
// data received through socket
|
||||
function handleOpponentMove(data) {
|
||||
let { from, to } = data;
|
||||
console.log(from, to, chess.get(to), chess.ascii());
|
||||
if (!chess.get(to)) {
|
||||
if (!chessRef.current.get(to)) {
|
||||
dispatch({ type: MOVE_PIECE, val: { from, to } });
|
||||
moveAudioRef.current.play();
|
||||
console.log(chess.ascii())
|
||||
return;
|
||||
} else {
|
||||
dispatch({ type: CAPTURE_PIECE, val: { from, to } });
|
||||
captureAudioRef.current.play();
|
||||
console.log(chess.ascii())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// called when user clicks a square
|
||||
function handleSquareClick(square, emitToSocketCallback) {
|
||||
let { type, color } = chess.get(square);
|
||||
let marked = moveHints.includes(square);
|
||||
let { type, color } = chessRef.current.get(square);
|
||||
let marked = moveHintsRef.current.includes(square);
|
||||
|
||||
if (chess.turn() === myColor) {
|
||||
if (chessRef.current.turn() === myColor) {
|
||||
if (type && color === myColor) {
|
||||
return dispatch({ type: SELECT_PIECE, val: square });
|
||||
}
|
||||
if (!type && selected && marked) {
|
||||
if (!type && selectedRef.current && marked) {
|
||||
dispatch({ type: MOVE_PIECE, val: { from: selected, to: square } })
|
||||
emitToSocketCallback({ from: selected, to: square })
|
||||
emitToSocketCallback({ from: selectedRef.current, to: square })
|
||||
setIsTimerOn(false)
|
||||
captureAudioRef.current.play();
|
||||
}
|
||||
if (type && marked) {
|
||||
dispatch({ type: CAPTURE_PIECE, val: { from: selected, to: square } })
|
||||
emitToSocketCallback({ from: selected, to: square })
|
||||
dispatch({ type: CAPTURE_PIECE, val: { from: selectedRef.current, to: square } })
|
||||
emitToSocketCallback({ from: selectedRef.current, to: square })
|
||||
setIsTimerOn(false);
|
||||
moveAudioRef.current.play();
|
||||
}
|
||||
@@ -159,8 +162,8 @@ const ChessGameContextProvider = ({ children }) => {
|
||||
function handleDrop(moveData) {
|
||||
let { from, to } = moveData;
|
||||
// console.log(from, to, ch ess.get(to), chess.ascii())
|
||||
if (moveHints.includes(to)) {
|
||||
if (chess.get(to)) {
|
||||
if (moveHintsRef.current.includes(to)) {
|
||||
if (chessRef.current.get(to)) {
|
||||
dispatch({ type: CAPTURE_PIECE, val: { from: from, to: to } }); // capture piece
|
||||
captureAudioRef.current.play();
|
||||
// setIsTimerOn(false)
|
||||
@@ -175,17 +178,17 @@ const ChessGameContextProvider = ({ children }) => {
|
||||
}
|
||||
|
||||
function selectPiece({ square, color: pieceColor }) {
|
||||
if (pieceColor === myColor && myColor === chess.turn()) {
|
||||
if (pieceColor === myColor && myColor === chessRef.current.turn()) {
|
||||
dispatch({ type: SELECT_PIECE, val: square });
|
||||
}
|
||||
}
|
||||
|
||||
function getSquareColor(square) {
|
||||
return chess.squareColor(square) === 'light' ? "w" : "b";
|
||||
return chessRef.current.squareColor(square) === 'light' ? "w" : "b";
|
||||
}
|
||||
|
||||
function isSquareMarked(square) {
|
||||
return moveHints.includes(square);
|
||||
return moveHintsRef.current.includes(square);
|
||||
}
|
||||
|
||||
function jumpTo(index) {
|
||||
@@ -193,24 +196,24 @@ const ChessGameContextProvider = ({ children }) => {
|
||||
}
|
||||
|
||||
function getChessBoard() {
|
||||
if (currentIndex === -1 || gameHistory.length === 0) {
|
||||
if (currentIndexRef.current === -1 || gameHistoryRef.current.length === 0) {
|
||||
return new ChessModified().getBoard();
|
||||
} else {
|
||||
// console.log(chess);
|
||||
let currentChessBoard = new ChessModified(gameHistory[currentIndex].fen).getBoard();
|
||||
let currentChessBoard = new ChessModified(gameHistoryRef.current[currentIndexRef.current].fen).getBoard();
|
||||
return currentChessBoard;
|
||||
}
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
if (currentIndex > 0) {
|
||||
jumpTo(currentIndex - 1);
|
||||
if (currentIndexRef.current > 0) {
|
||||
jumpTo(currentIndexRef.current - 1);
|
||||
}
|
||||
}
|
||||
|
||||
function goAhead() {
|
||||
if (currentIndex < gameHistory.length - 1) {
|
||||
jumpTo(currentIndex + 1);
|
||||
if (currentIndexRef.current < gameHistoryRef.current.length - 1) {
|
||||
jumpTo(currentIndexRef.current + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import { useReducer } from "react";
|
||||
|
||||
import { ChessModified, chessInit } from "../utils/chess"
|
||||
import { DISPATCH_EVENTS } from "../constants";
|
||||
|
||||
const { SELECT_PIECE, MOVE_PIECE, CAPTURE_PIECE, SET_GAME_HISTORY } = DISPATCH_EVENTS
|
||||
|
||||
let BLACK = 'b', WHITE = 'w';
|
||||
|
||||
function chessStateInit() {
|
||||
let chess = chessInit();
|
||||
let moveHints = [];
|
||||
let gameHistory = [];
|
||||
let capturedPieces = { 'w': [], 'b': [] };
|
||||
let selected = null;
|
||||
// represents which move is viewed in history
|
||||
let currentHistoryIndex = -1;
|
||||
let hasEnded = false;
|
||||
|
||||
return { chess, moveHints, selected, gameHistory, currentHistoryIndex, hasEnded, capturedPieces }
|
||||
}
|
||||
|
||||
const reducer = (state, action) => {
|
||||
console.log(action);
|
||||
switch (action.type) {
|
||||
case SELECT_PIECE: {
|
||||
let moveHints = state.chess.getMoves(action.val);
|
||||
let selected = action.val
|
||||
return { ...state, moveHints, selected };
|
||||
}
|
||||
case MOVE_PIECE: {
|
||||
let newChess = new ChessModified(state.chess.fen());
|
||||
console.log(newChess.ascii());
|
||||
let updatedGameHistory = [...state.gameHistory];
|
||||
let { san, after } = newChess.move(action.val);
|
||||
updatedGameHistory.push({ move: san, fen: after });
|
||||
return { ...state, chess: newChess, moveHints: [], gameHistory: updatedGameHistory, currentHistoryIndex: updatedGameHistory.length - 1, selected: null };
|
||||
}
|
||||
case CAPTURE_PIECE: {
|
||||
let newChess = new ChessModified(state.chess.fen());
|
||||
let updatedGameHistory = [...state.gameHistory];
|
||||
let { san, after } = newChess.move(action.val);
|
||||
updatedGameHistory.push({ move: san, fen: after });
|
||||
return { ...state, chess: newChess, moveHints: [], gameHistory: updatedGameHistory, currentHistoryIndex: updatedGameHistory.length - 1, selected: null };
|
||||
}
|
||||
case SET_GAME_HISTORY: {
|
||||
let fetchedGameHistory = action.val;
|
||||
let newChess = new ChessModified();
|
||||
let updatedGameHistory = [];
|
||||
let capturedPieces = { 'w': [], 'b': [] }
|
||||
for (let i = 0; i < fetchedGameHistory.length; i++) {
|
||||
let { san, after, captured, color } = newChess.move(fetchedGameHistory[i]);
|
||||
updatedGameHistory.push({ fen: after, move: san });
|
||||
if (captured) {
|
||||
color === WHITE ? capturedPieces[BLACK].push(captured) : capturedPieces[WHITE].push(captured);
|
||||
}
|
||||
}
|
||||
return { ...state, chess: newChess, gameHistory: updatedGameHistory, currentHistoryIndex: updatedGameHistory.length - 1, capturedPieces }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const useChess = () => {
|
||||
const [{ chess, moveHints, selected, gameHistory, currentHistoryIndex, capturedPieces, hasEnded }, dispatch] = useReducer(reducer, null, chessStateInit);
|
||||
|
||||
function selectPiece(square) {
|
||||
dispatch({ type: SELECT_PIECE, val: square })
|
||||
}
|
||||
|
||||
function movePiece(from, to) {
|
||||
dispatch({ type: MOVE_PIECE, val: { from, to } })
|
||||
}
|
||||
|
||||
function capturePiece(from, to) {
|
||||
dispatch({ type: CAPTURE_PIECE, val: { from, to } })
|
||||
}
|
||||
|
||||
function setGameHistory(gameHistory) {
|
||||
dispatch({ type: SET_GAME_HISTORY, val: gameHistory })
|
||||
}
|
||||
|
||||
return {
|
||||
chessState: {
|
||||
chess, selected, moveHints, gameHistory, currentHistoryIndex, capturedPieces, hasEnded
|
||||
},
|
||||
chessStateModifiers: {
|
||||
selectPiece, movePiece, capturePiece, setGameHistory
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default useChess;
|
||||
@@ -11,10 +11,11 @@ import ChessBoard from '../Chess/ChessBoard'
|
||||
import GameHistory from '../../components/GameHistory'
|
||||
import MainLoader from '../../components/MainLoader'
|
||||
import { SOCKET_EVENTS } from '../../constants'
|
||||
import Timer from './Timer'
|
||||
const { CONNECT, DISCONNECT, CHESS_OPPONENT_MOVE, USER_RESIGNED, JOIN_ROOM, JOIN_ROOM_ERROR, JOIN_ROOM_SUCCESS, ROOM_FULL, USER_JOINED_ROOM } = SOCKET_EVENTS;
|
||||
|
||||
const ChessGame = () => {
|
||||
const { setGameHistory, hasGameEnded, gameEndedReason, endGame } = useContext(ChessGameContext);
|
||||
const { setGameHistory, hasGameEnded, gameEndedReason, endGame, handleOpponentMove, isTimerOn } = useContext(ChessGameContext);
|
||||
const [gameEndedModalOpen, modalFunctions] = useDisclosure(true);
|
||||
|
||||
const user = getUserData();
|
||||
@@ -26,6 +27,7 @@ const ChessGame = () => {
|
||||
const roomID = localStorage.getItem('roomID');
|
||||
const navigate = useNavigate();
|
||||
const opponent = localStorage.getItem('opponent');
|
||||
let connected = socket.id;
|
||||
|
||||
const exitGame = () => {
|
||||
// cleanup game related data
|
||||
@@ -69,10 +71,7 @@ const ChessGame = () => {
|
||||
console.log('Socket disconnected due to', reason);
|
||||
});
|
||||
|
||||
socket.on(CHESS_OPPONENT_MOVE, () => {
|
||||
// console.log(data);
|
||||
// setIsTimerOn(true);
|
||||
})
|
||||
socket.on(CHESS_OPPONENT_MOVE, handleOpponentMove)
|
||||
|
||||
socket.on(USER_JOINED_ROOM, () => {
|
||||
setIsWaiting(false);
|
||||
@@ -88,6 +87,10 @@ const ChessGame = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('Connection useEffect()');
|
||||
}, [connected]);
|
||||
|
||||
if (!hasJoinedRoom) return (
|
||||
<MainLoader />
|
||||
)
|
||||
@@ -111,7 +114,7 @@ const ChessGame = () => {
|
||||
</Avatar>}
|
||||
description={"description"}
|
||||
/>
|
||||
{/* <Timer on={!isTimerOn} /> */}
|
||||
<Timer on={!isTimerOn} />
|
||||
</div>
|
||||
{
|
||||
// TODO: handle isWaiting state
|
||||
@@ -137,7 +140,7 @@ const ChessGame = () => {
|
||||
</Avatar>}
|
||||
description={"description"}
|
||||
/>
|
||||
{/* <Timer on={isTimerOn} /> */}
|
||||
<Timer on={isTimerOn} />
|
||||
</div>
|
||||
</Flex>
|
||||
<MediaQuery smallerThan="lg" styles={{ display: 'none' }}>
|
||||
|
||||
Reference in New Issue
Block a user