使用React Hook的setState更新二维数组致应用崩溃
React最短路径可视化网格点击崩溃问题修复
问题根源分析
你的代码导致应用崩溃主要有三个核心问题:
- 事件处理函数直接执行:渲染网格时
onClick={handleMouseDown(rowIndex, columnIndex)}会立即调用函数,而非点击时触发,引发无限状态更新循环。 - 二维数组遍历错误:
handleMouseDown中用grid.map((node) =>遍历的是整行而非单个节点,无法正确定位目标单元格。 - 违反状态不可变性:直接修改原节点对象属性,React无法检测到状态变化,还会导致状态不一致。
修正后的完整代码
import './App.css'; import { useState } from "react"; const start_row = 9; const start_col = 9; const end_row = 20; const end_col = 50; function App() { const [darkMode, setDarkMode] = useState(true); function createNode(row, col) { return { row, col, isStart: row === start_row && col === start_col, isFinish: row === end_row && col === end_col, isWall: false, // 重命名为更语义化的isWall }; } const createGrid = (rows, cols) => { const grid = []; for (let row = 0; row < rows; row++) { const currentRow = []; for (let col = 0; col < cols; col++) { currentRow.push(createNode(row, col)); } grid.push(currentRow); } return grid; } // 用初始化函数避免每次渲染重复创建初始网格 const [grid, setGrid] = useState(() => createGrid(30, 65)); const handleClick = () => { setDarkMode(!darkMode); } const setMode = (style) => { let backgroundColor, textColor, buttonColor; if (darkMode) { backgroundColor = "black"; textColor = 'white'; buttonColor = "#303030"; } else { backgroundColor = 'white'; textColor = 'black'; buttonColor = "#ffe291"; } if (style === 1) return backgroundColor; if (style === 2) return textColor; return buttonColor; } const drawNode = (node) => { if (node.isStart) { return <img src="https://img.icons8.com/ios-glyphs/30/null/play--v1.png" alt="起点"/>; } if (node.isFinish) { return <img src="https://img.icons8.com/ios-glyphs/30/null/finish-flag.png" alt="终点"/> } if (node.isWall) { return <div className="Wall"></div> } } const nodeName = (row, col) => { return `node ${row}-${col}`; } const handleMouseDown = (targetRow, targetCol) => { // 正确遍历二维数组,严格遵循状态不可变性 const newGrid = grid.map((row, rowIndex) => { if (rowIndex !== targetRow) return [...row]; // 非目标行直接复制 return row.map((node, colIndex) => { if (colIndex !== targetCol) return {...node}; // 非目标节点直接复制 // 创建新节点对象切换墙体状态 return { ...node, isWall: !node.isWall }; }); }); setGrid(newGrid); } return ( <div className="App" style={{ backgroundColor: setMode(1), color: setMode(2) }}> <div className="header-container"> <div className="darkmode-container"> <label className="darkmode-button" style={{ backgroundColor: setMode(3) }}> <input type="checkbox" className="darkmode-input" onClick={handleClick} /> <span className="darkmode-toggle"> <img className="moon" src="https://img.icons8.com/sf-regular/48/null/moon-symbol.png" alt="月亮"/> </span> <img className="sun" src="https://img.icons8.com/material-outlined/24/000000/smiling-sun.png" alt="太阳"/> </label> </div> <div className="header"> <header>Visualizer</header> </div> </div> <div className="map-container"> <div className="map"> {grid.map((row, rowIndex) => ( row.map((node, colIndex) => ( <div className={nodeName(rowIndex, colIndex)} onClick={() => handleMouseDown(rowIndex, colIndex)} key={`${rowIndex}-${colIndex}`} // 添加key避免React警告 > {drawNode(node)} </div> )) ))} </div> </div> </div> ); } export default App;
关键修改说明
- 事件回调修正:将
onClick改为箭头函数传递,确保点击时才触发处理函数 - 状态不可变性:创建新的行和节点对象修改状态,避免直接修改原对象
- 遍历逻辑修复:正确遍历二维数组,定位目标单元格
- 语义化优化:将
isMousePressed重命名为isWall,更贴合功能需求 - 性能优化:用
useState初始化函数创建初始网格,避免重复执行 - 添加key属性:解决React列表渲染的key警告
内容的提问来源于stack exchange,提问作者vmeesala
相关产品推荐
相关产品推荐

