React读取localStorage报insertRow空错误 二次点击返回首值undefined
问题根因
- 触发
insertRow报错是因为React状态更新为异步执行:调用setTable_show_Deposit、setHistory等修改状态的方法后,DOM不会立刻渲染,此时直接用document.getElementById获取表格DOM会返回null,调用方法自然报错。第二次点击时对应状态已经为true,DOM已经渲染完成,所以可以正常获取元素执行后续逻辑。 - 返回第一个值为undefined有两个原因:
- 初始化
collections_transaction_deposite、collections_transaction_withdraw两个状态时,初始值设置为[{}],数组默认携带了一个空对象,后续存入localStorage、读取渲染时这个空对象对应的属性自然都是undefined。 - 调用setState更新交易数组后,立刻将当前数组存入localStorage,而setState是异步的,此时拿到的还是更新前的旧数组,导致存储的内容永远比实际操作少一条,也会出现取值异常。
- 初始化
修复方案
1. 修复异步渲染导致DOM获取失败问题
把操作DOM的逻辑放到setTimeout里,等状态更新、DOM渲染完成后再执行,也可以用useEffect监听对应状态的变化,状态变更后再执行DOM操作。以下为修改成本最低的setTimeout方案,以Deposite_show_table方法为例:
const Deposite_show_table = () => { setTable_show_Deposit(true); setTable_show_WithDraw(false); // 加setTimeout等DOM渲染完成 setTimeout(() => { if (table !== "") { const table = document.getElementById('deposite_table'); // 加DOM存在判断,避免极端情况报错 if (!table) return myArr = JSON.parse(localStorage.getItem('transaction_Deposite')) || []; console.log(myArr); for (let i = 0; i < myArr.length; i++) { const row = table.insertRow(i); row.innerHTML = "<td>" + myArr[i].transaction_type + "</td>" + "<td>" + myArr[i].Amount + "</td>" + "<td>" + myArr[i].time + "</td>" + "<td>" + myArr[i].Balance + "</td>" + "<td>" + myArr[i].interest + "</td>" } } else { console.log("Empty"); } }, 0) }
WithDraw_show_table、RecentTrans方法都按同样的逻辑修改,把操作DOM的代码包裹在setTimeout里即可。
2. 修复第一个值为undefined问题
- 把两个交易数组的初始值改成空数组:
// 原错误写法 // const [collections_transaction_deposite, setCollections_transaction_deposite] = useState([obj]); // const [collections_transaction_withdraw, setCollections_transaction_withdraw] = useState([object]); // 修改后 const [collections_transaction_deposite, setCollections_transaction_deposite] = useState([]); const [collections_transaction_withdraw, setCollections_transaction_withdraw] = useState([]);
- 把localStorage存储逻辑放到setState的回调里,确保存的是更新后的数组,以
Deposite方法为例:
const Deposite = () => { const event = new Date().toLocaleString(); const setter = localStorage.getItem(getter) const store = parseInt(setter) + parseInt(init); localStorage.setItem(getter, store); let interset_Amount = ''; if (store < 0) { interset_Amount = store; } else { interset_Amount = 0; } const newObj = { "transaction_type": 'Deposite', "Amount": document.getElementById('depinput').value, "time": event, "Balance": store, "interest": interset_Amount } // setState回调里存localStorage,确保取到最新数组 setCollections_transaction_deposite(pre => { const newArr = [newObj, ...pre] localStorage.setItem("transaction_Deposite", JSON.stringify(newArr)) return newArr }) }
Withdraw方法按同样逻辑修改即可。
补充优化建议
不建议直接操作DOM插入表格行,符合React开发规范的写法是直接把交易数组的渲染逻辑写到JSX里,不需要手动操作DOM,能从根本上避免这类异步渲染导致的DOM操作问题。
内容的提问来源于stack exchange,提问作者Hariharan A
相关产品推荐
相关产品推荐

