如何让React useEffect在智能合约调用后正确显示更新后的变量?
问题根因
你当前的代码存在基础逻辑错误:你把useEffect内部修改的状态变量tetherBalance、amountDeposited加入了依赖数组,这会导致每次状态更新后都会重新触发useEffect执行,形成无限重跑的死循环,反而会干扰正常的状态更新逻辑。同时你没有针对存币交易的完成时机做主动的状态刷新,自然无法拿到链上更新后的最新数据。
可行解决方案
1. 先修正useEffect依赖逻辑
首先把拉取用户数据的逻辑抽成可复用的公共函数,同时移除useEffect中不需要的依赖项,只保留会影响数据拉取逻辑的外部变量:
function App() { const [tether, setTether] = React.useState([undefined]); const [tetherAddress, setTetherAddress] = React.useState(""); const [tetherBalance, setTetherBalance] = React.useState("0"); const [amountDeposited, setAmountDeposited] = React.useState("0"); window.web3 = new Web3(window.web3.currentProvider); // 抽离为公共函数,多处可调用 const fetchUserData = async (currentAccount) => { if (!currentAccount || !tether || !chadsVault) return const tetherBalance = await tether.methods.balanceOf(currentAccount).call() setTetherBalance(tetherBalance.toString()) const deposited = await chadsVault.methods.amountDeposited(currentAccount).call() setAmountDeposited(deposited.toString()) } useEffect(() => { // 只有账户、合约实例变化时才初始化拉取数据 fetchUserData(account) }, [account, tether, chadsVault]) // 只保留外部依赖,不要放内部会修改的状态
2. 存币交易确认后主动触发数据刷新
不需要靠useEffect被动监听变化,在用户发起存币的逻辑中,等待交易上链确认后直接调用fetchUserData即可实现无刷新更新:
const handleDeposit = async (depositAmount) => { // 第一步:先授权合约使用ERC20额度(如果需要的话) await tether.methods.approve(chadsVaultAddress, depositAmount).send({ from: account }) // 第二步:调用存币方法,等待交易确认 const depositTx = await chadsVault.methods.deposit(depositAmount).send({ from: account }) // 交易被区块确认后,主动拉取最新数据 if (depositTx.status) { await fetchUserData(account) } }
3. 可选:订阅合约事件实现自动更新
如果想要实现其他地址给当前账户转币、或者其他链上操作触发余额变化时也能自动更新,可以订阅合约的相关事件,收到事件后自动拉取数据:
useEffect(() => { if (!account || !tether || !chadsVault) return // 订阅ERC20的Transfer事件,筛选接收方为当前账户的交易 const transferEvent = tether.events.Transfer({ filter: { to: account }, fromBlock: 'latest' }).on('data', async () => { await fetchUserData(account) }) // 订阅质押合约的Deposit事件 const depositEvent = chadsVault.events.Deposit({ filter: { user: account }, fromBlock: 'latest' }).on('data', async () => { await fetchUserData(account) }) // 组件卸载时取消订阅避免内存泄漏 return () => { transferEvent.unsubscribe() depositEvent.unsubscribe() } }, [account, tether, chadsVault]) }
内容的提问来源于stack exchange,提问作者Bdellinger
相关产品推荐
相关产品推荐

