React中修改div的className后界面未更新的问题
问题分析与解决
问题现象
尝试通过每秒修改数组状态切换div的className来改变背景色,console.log(grid[0][0])显示数据已修改,但首次迭代后div外观不再更新。index.js中传入props:<App row={5} col={10}/>。
代码示例
App.js
import React, { useState, useEffect } from 'react'; import './App.css'; function App(props) { // Initializing the grid. let grid = initializeGrid(props.row, props.col); const [gridData, setGridData] = useState(grid); useEffect(() => { const interval = setInterval(() => { setGridData(gridData => updateGrid(gridData)); }, 2000); return () => clearInterval(interval); }, []); return ( <div className="container"> {gridData.reduce(function (acc, itm, idx) { let item = itm.map((cell, index) => <div key={acc.length + index} className={cell.state ? "state-on" : "state-off"}>{acc.length + index}</div>); return acc.concat(item); }, [])} </div> ); } function initializeGrid(row, col) { let cell = { state: false, count: 0 } let grid = []; for (let y = 0; y < row; y++) { grid.push([]); for (let x = 0; x < col; x++) { grid[y].push(cell); } } return grid; } function updateGrid(grid) { let currentState = grid[0][0].state; currentState ? (grid[0][0] = { state: false, count: 0 }) : (grid[0][0] = { state: true, count: 0 }); console.log(grid[0][0]); return grid; } export default App;
App.css
html, body, #root { box-sizing: border-box; background-color: #ffeead; height: 100%; padding: 10px; margin: 0px; } .container { height: 100%; display: grid; grid-gap: 3px; grid-template-columns: repeat(10, 80px); grid-template-rows: repeat(5, 80px); justify-content: center; align-content: center; } .container>div { display: flex; justify-content: center; align-items: center; font-size: 2em; color: #ffeead; } .state-off { background-color: #96ceb4; } .state-on { background-color: #ff6f69; }
问题根源
- 状态更新未改变引用:React的
useState依赖浅比较判断状态是否变化。updateGrid直接修改原数组元素后返回原数组,数组引用未变,React认为状态无更新,因此首次渲染后不再触发重绘。 - 初始化单元格引用重复:
initializeGrid中所有单元格复用同一个cell对象,导致所有单元格共享同一状态,会引发后续潜在问题。
修复方案
1. 修复updateGrid,创建新数组与对象
修改updateGrid,通过拷贝数组和对象的方式确保返回新引用,让React检测到状态变化:
function updateGrid(grid) { // 拷贝二维数组的每一行,避免直接修改原数组 const newGrid = grid.map(row => [...row]); const currentState = newGrid[0][0].state; // 创建新的单元格对象替换目标位置 newGrid[0][0] = { state: !currentState, count: 0 }; console.log(newGrid[0][0]); return newGrid; }
2. 修复initializeGrid,每个单元格创建独立对象
修改初始化函数,确保每个单元格都是独立对象,避免引用共享:
function initializeGrid(row, col) { let grid = []; for (let y = 0; y < row; y++) { grid.push([]); for (let x = 0; x < col; x++) { // 每次循环创建新的cell对象 grid[y].push({ state: false, count: 0 }); } } return grid; }
完整修复后的App.js
import React, { useState, useEffect } from 'react'; import './App.css'; function App(props) { // 用函数式初始化避免组件重渲染时重复执行initializeGrid const [gridData, setGridData] = useState(() => initializeGrid(props.row, props.col)); useEffect(() => { const interval = setInterval(() => { setGridData(gridData => updateGrid(gridData)); }, 2000); return () => clearInterval(interval); }, []); return ( <div className="container"> {gridData.reduce(function (acc, itm, idx) { let item = itm.map((cell, index) => <div key={acc.length + index} className={cell.state ? "state-on" : "state-off"}>{acc.length + index}</div>); return acc.concat(item); }, [])} </div> ); } function initializeGrid(row, col) { let grid = []; for (let y = 0; y < row; y++) { grid.push([]); for (let x = 0; x < col; x++) { grid[y].push({ state: false, count: 0 }); } } return grid; } function updateGrid(grid) { const newGrid = grid.map(row => [...row]); const currentState = newGrid[0][0].state; newGrid[0][0] = { state: !currentState, count: 0 }; console.log(newGrid[0][0]); return newGrid; } export default App;
说明
- 状态更新时必须返回新的数组/对象引用,React才能识别到状态变化并触发重渲染。
- 初始化数组时避免复用对象引用,确保每个元素都是独立实例,防止意外的状态同步。
内容的提问来源于stack exchange,提问作者Akbar
相关产品推荐
相关产品推荐

