React StrictMode下随机色块网格点击失效问题排查
问题原因与修复方案
为啥会出现这问题?
React StrictMode在开发环境里会重复执行组件初始化、状态更新相关的钩子(比如useState的初始函数、useReducer、useEffect),还会严格检查状态更新的不可变性。如果你的状态更新逻辑违反了不可变原则(比如直接修改原数组/对象就返回),或者依赖了没处理好的可变外部变量,就会导致StrictMode下后续状态更新时,React觉得状态没变化,所以不重新渲染界面。
常碰到的坑和修复办法
1. 直接修改原数组/对象的状态更新逻辑
比如处理色块移动时,直接改了原网格数组,然后用setGrid(grid)更新状态:
// 错例:直接修改原数组 const handleBlockClick = (row, col) => { // 查找空白位置 let emptyRow, emptyCol; for (let i = 0; i < grid.length; i++) { const idx = grid[i].findIndex(c => c === 'black'); if (idx !== -1) { emptyRow = i; emptyCol = idx; break; } } // 直接修改原数组内容 grid[row][col] = 'black'; grid[emptyRow][emptyCol] = grid[row][col]; setGrid(grid); // 传递原数组引用,React判定状态未变更 };
修复:创建新的数组引用
每次更新状态都要生成新的数组副本,不能直接修改原数组:
// 正确写法:创建新数组副本 const handleBlockClick = (row, col) => { // 查找空白位置坐标 let emptyRow, emptyCol; for (let i = 0; i < grid.length; i++) { const idx = grid[i].findIndex(c => c === 'black'); if (idx !== -1) { emptyRow = i; emptyCol = idx; break; } } // 仅处理与空白位置相邻的色块点击 if (Math.abs(row - emptyRow) + Math.abs(col - emptyCol) !== 1) return; // 生成网格数组的深副本 const newGrid = grid.map(row => [...row]); // 交换色块与空白位置 newGrid[row][col] = 'black'; newGrid[emptyRow][emptyCol] = grid[row][col]; // 更新状态 setGrid(newGrid); };
2. 用外部可变变量初始化状态
比如你用一个外部的可变数组来初始化useState,StrictMode的双重调用会导致初始状态被意外修改:
// 错例:外部可变变量作为初始值 let initialGrid = generateRandomGrid(); // 外部可变变量 const [grid, setGrid] = useState(initialGrid);
修复:用函数初始化状态
把初始状态的生成放在useState的初始化函数中,保证每次初始化都是独立的:
// 正确写法:用函数生成初始状态 const [grid, setGrid] = useState(() => generateRandomGrid());
3. useEffect未清理副作用
如果你的useEffect添加了事件监听或创建了可变引用,但未清理,StrictMode的双重调用会留下副作用残留,影响后续状态更新:
// 错例:未清理事件监听 useEffect(() => { window.addEventListener('keydown', handleKeyPress); // 缺少清理函数 }, []);
修复:添加清理函数
确保useEffect的清理函数能移除订阅或重置可变状态:
// 正确写法:添加清理函数 useEffect(() => { window.addEventListener('keydown', handleKeyPress); return () => { window.removeEventListener('keydown', handleKeyPress); }; }, [handleKeyPress]);
额外排查点
- 检查状态更新的条件逻辑:确保每次点击都确实触发了状态变化(比如只有相邻色块才允许移动,避免把无效点击误判为无更新)
- 核对组件依赖项:比如用
useCallback包裹的点击函数,是否正确添加了依赖数组,避免因函数引用不变导致后续调用无法触发状态更新
内容的提问来源于stack exchange,提问作者Oni Barolli
相关产品推荐
相关产品推荐

