React 的严格模式双重渲染显示不一致的状态



我知道React在开发中以严格模式渲染两次,这是为了确保功能是纯粹的,没有副作用。然而,在我的情况下,它似乎不一致,而得到的状态。似乎在回放函数时,它有1个更新的状态参数和1个未更新的状态参数。我知道这可能没有意义,所以这里有一些带有调试信息的代码。这是一个基本的井字游戏

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]

日志的前两行显示初始状态,然后是"else"取路径,即标记板上的框。到目前为止一切顺利。
接下来的两行显示了开发中严格模式下的重放。这应该可以正常工作,因为我们没有直接改变状态。然而,正如你在日志中看到的,它打印的板是更新的板,即X在第一行和col中标记,但nextPlayer没有更新为"O"。为什么?

如果重播使用更新状态,nextPlayer最初应该是'O'。
如果重放不使用更新状态,则该板仍应为初始板。

我对React有点陌生,我花了很多时间调试这个。我需要帮助或解释。如果有什么不清楚的地方,请告诉我。

let board = [...state.board]创建板的浅拷贝

board是一个新数组,但嵌套的数组是相同的,所以

board[action.payload.y][action.payload.x] = state.nextPlayer

编辑旧数组


创建深拷贝使用:

let board = state.board.map(row => [...row])

最新更新