React setState状态更新失效 无法获取链上账户余额
React类组件setState不更新、链上余额无法渲染修复方案
核心问题原因
setState本身是异步批量执行的,你在loadWeb3Data中连续调用三次setState存储合约实例后,立刻同步调用fetchAccountInfo,此时React还未完成状态更新,this.state.daiContract、this.state.ethers仍然是初始化的null值,调用balanceOf方法会直接抛出异常,后续更新余额的逻辑根本不会执行,页面始终显示初始的walletBalance: "5"。- 使用了React已标记为废弃、存在执行时序风险的
componentWillMount生命周期做异步请求,在React18版本中该方法的执行逻辑会导致状态更新异常。 - 直接在
setState调用后同步打印this.state,本身就只能拿到更新前的旧状态,属于React异步更新的正常机制,不是bug。 - 初始化合约实例时未传入签名者,部分RPC节点会拦截关联当前钱包地址的查询请求,返回异常数据。
修复步骤
- 替换废弃生命周期
删除原有的componentWillMount,使用React官方推荐的副作用执行生命周期componentDidMount:
componentDidMount() { this.loadWindow() }
- 调整状态更新时序,避开异步读旧值问题
不要在调用setState后立刻从this.state读取刚设置的值,合约实例初始化后先存在局部变量中,直接传入fetchAccountInfo使用,等链上数据查询完成后再批量更新状态,避免时序问题:
async loadWeb3Data(){ // 原有ABI定义部分保持不变 const provider=new providers.Web3Provider(window.ethereum) const signer = provider.getSigner() // 获取签名者,兼容后续合约写入操作 window.alert("Start") // 合约实例先存局部变量,不立即setState const daiContract=new Contract("0x6B175474E89094C44Da98b954EedeAC495271d0F",daiAbi,signer); const cDaiContract= new Contract("0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643",cDaiAbi,signer) const AaveLendingPoolContract=new Contract("0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9",AaveLendingPoolAbi,signer) // 直接把需要的参数传入方法,不依赖this.state的即时更新 await this.fetchAccountInfo({ daiContract, defaultAccount: this.state.defaultAccount, ethersIns: ethers }) // 数据查询完成后批量更新合约实例到state this.setState({ daiContract, cDaiContract, AaveLendingPoolContract }) }
- 修改余额查询逻辑,不依赖即时state
async fetchAccountInfo({daiContract, defaultAccount, ethersIns}){ let walletBalance=await daiContract.balanceOf(defaultAccount) window.alert("stop") walletBalance = ethersIns.utils.formatEther(walletBalance, 'ether') // 如需打印最新状态,放在setState回调中 this.setState({walletBalance}, () => { console.log('最新状态:', this.state) }); }
- 清理render方法中的无效注释
删除JSX中包裹h2标签的//////////和===>>>标记,避免语法解析异常,保留余额渲染标签即可:
<center><img className='Diag' src={bodyImage} alt="blockchain" /></center> <h2>{this.state.walletBalance}</h2>
额外检查项
- 确认浏览器钱包已切换到以太坊主网,你代码中写的DAI、cDAI、Aave借贷池地址都是主网地址,切换到其他网络会导致合约调用失败。
- 发起钱包连接请求时确认用户已授权账户访问,否则
defaultAccount为null也会导致查询失败。
内容的提问来源于stack exchange,提问作者Azzor
相关产品推荐
相关产品推荐

