React setState数组Hook无法触发组件重渲染问题排查
React折叠表格不触发重渲染的问题解决
你遇到的核心问题是直接修改了React state的原引用,React依赖state的引用变化来触发重渲染,当你直接修改原数组/对象时,React检测不到变化,所以不会更新视图。
代码里的具体问题
- 直接复用state引用:
let temp = dataShow;这行只是把原state数组的引用赋值给temp,后续对temp的修改其实就是在修改原state,React认为数组引用没变化,不会触发重渲染。 - 可变操作修改对象:不管是
delete temp[idx].var还是temp[idx]['var'] = [],都是直接修改原state里的对象,没有创建新的对象/数组副本,React无法感知到变化。 - map参数顺序错误:
row.var.map((var_idx, var_value) =>这里参数顺序搞反了,map的第一个参数是数组元素,第二个是索引,会导致key生成错误,影响渲染。
修复后的代码
JavaScript部分
const data = { 'T-Shirt Minion': { 'total': 571, 'var': { 'Lengan Panjang': { 'total': 431, 'colour': { 'Merah': { 'total': 431, 'size': { 'XL': 231, 'S': 100, 'M': 100 } } } }, 'Lengan Pendek': { 'total': 140, 'colour': { 'Merah': { 'total': 140, 'size': { 'L': 140 } } } } } } }; function MyApp() { const [dataShow, setDataShow] = React.useState([]); React.useEffect(() => { // 初始化时用不可变方式生成初始数据 const initialData = Object.keys(data).map(key => ({ name: key, total: data[key].total })); setDataShow(initialData); }, []); const handleNameClick = (idx, name) => { setDataShow(prev => { // 创建原数组的副本,避免修改原state引用 const newDataShow = [...prev]; // 复制要修改的行,生成新对象 const targetRow = {...newDataShow[idx]}; if (targetRow.var) { // 折叠:删除var属性,返回新对象 delete targetRow.var; } else { // 展开:从原数据中提取var数据并转为数组 const varData = data[name]['var']; targetRow.var = Object.keys(varData).map(key => ({ name: key, total: varData[key].total })); } // 替换副本中的目标行 newDataShow[idx] = targetRow; return newDataShow; }); }; return ( <table border="1"> <thead> <tr> <th> </th> <th>Name</th> <th>Variant</th> <th>Colour</th> <th>Size</th> <th>Stock</th> </tr> </thead> <tbody> {dataShow.map((row, index) => ( <React.Fragment key={`${index}-${row.name}`}> <tr> <td style={{ cursor: "pointer" }} onClick={() => handleNameClick(index, row.name)}> {/* 动态切换展开/折叠按钮文本 */} {row.var ? '-' : '+'} </td> <td colSpan={4} >{row.name}</td> <td>{row.total}</td> </tr> {row.var ? row.var.map((var_value, var_idx) => ( <React.Fragment key={`${index}-${var_idx}-${var_value.name}`}> <tr> <td style={{ cursor: "pointer" }}> + </td> <td> </td> <td colSpan={3}>{var_value.name}</td> <td>{var_value.total}</td> </tr> </React.Fragment> )) : null } </React.Fragment> ))} </tbody> </table> ); } ReactDOM.render( < MyApp / > , document.getElementById('root') );
HTML部分
<script src="https://unpkg.com/react@17/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> <div id="root"></div>
修复要点说明
- 所有state更新都基于prev state创建新的数组/对象副本,保证引用变化,触发React重渲染。
- 初始化state时用
map一次性生成数组,避免多次调用setDataShow。 - 修正了map的参数顺序,同时优化了key的生成规则,避免重复冲突。
- 新增展开/折叠按钮的文本切换逻辑,提升交互体验。
内容的提问来源于stack exchange,提问作者Anthony Lauly
相关产品推荐
相关产品推荐

