I'm aware React renders twice in Strict mode in development, this is to ensure the function is pure and there are no side effects. However in my case it seems to not be consistent while getting the state. It seems that when replaying the function, it has 1 updated state parameter and 1 non-updated. I understand this might not make sense, so here's some code with debugging information. Its a basic tic tac toe game
const tttReducer = (state, action) => {
// generate a random number to mark whether the replay is running or original
const running = Math.random()*100;
// print initial state
console.log(running, 'initial', 'nextplayer', state.nextPlayer, state.board[0],
state.board[1], state.board[2]);
// If state on the board has been set, return the state as it is
if (state.board[action.payload.y][action.payload.x] !== null) {
console.log(running, 'if', 'nextplayer', state.nextPlayer, state.board[0], state.board[1], state.board[2]);
return state
}
// Mark on the board, change the player and return
let board = [...state.board]
board[action.payload.y][action.payload.x] = state.nextPlayer
const nextPlayer = state.nextPlayer === 'X' ? 'O' : 'X'
console.log(running, 'else', 'nextplayer', nextPlayer, board[0], board[1], board[2]);
return {
...state,
board,
nextPlayer,
};
}
const [state, dispatch] = useReducer(tttReducer, {
board: [[null, null, null],
[null, null, null],
[null, null, null]],
nextPlayer: 'X',
winner: null,
})
19.995504281947607 'initial' 'nextplayer' 'X' (3) [null, null, null] (3) [null, null, null] (3) [null, null, null]
App.jsx:32 19.995504281947607 'else' 'nextplayer' 'O' (3) ['X', null, null] (3) [null, null, null] (3) [null, null, null]
react_devtools_backend.js:4066 7.972281733648767 initial nextplayer X (3) ['X', null, null] (3) [null, null, null] (3) [null, null, null]
react_devtools_backend.js:4066 7.972281733648767 if nextplayer X (3) ['X', null, null] (3) [null, null, null] (3) [null, null, null]
The first 2 lines of log show the initial state, and then the "else" path is taken, i.e. the box on the board is marked. So far so good.
The next 2 lines show the replay in Strict mode in development. This SHOULD work fine because we're not mutating state directly. However as you can see in the log, the board it is printing is the updated board, i.e. X is marked in the first row and col, BUT nextPlayer is NOT updated to be 'O'. Why?
If the replay uses updated state, the nextPlayer should be 'O' initially.
If the replay does not use the updated state, then the board should still be the initial board.
I'm a little new to React, and I've spent a lot of time debugging this. I'd love help or explanation. Please let me know if something is not clear here.
CodePudding user response:
let board = [...state.board]
create a shallow copy of the board
board
is a new array, but the nested arrays are the same so
board[action.payload.y][action.payload.x] = state.nextPlayer
edits the old array
To create a deep copy use:
let board = state.board.map(row => [...row])