如何监听MetaMask/ethers.js钱包余额变化?
监听React应用中账户余额变化的方案
不用依赖低效的setInterval,有几种更优雅的实时同步方案:
1. 后端主动推送(WebSocket/SSE)
这是最实时的方案,当后端检测到账户余额变化(比如充值完成),主动把新余额推送给前端。在React组件里可以这样实现:
import { useEffect, useState } from 'react'; function BalanceComponent() { const [balance, setBalance] = useState(0); useEffect(() => { // 建立WebSocket连接 const socket = new WebSocket('ws://your-backend-url/balance-updates'); // 监听后端推送的余额更新消息 socket.onmessage = (event) => { const newBalance = JSON.parse(event.data).balance; setBalance(newBalance); }; // 组件卸载时关闭连接 return () => socket.close(); }, []); return <div>当前余额:{balance}</div>; }
如果后端支持Server-Sent Events(SSE),也可以用EventSource,实现更轻量,适合单向推送的场景。
2. 全局状态管理订阅
如果用Redux、Zustand这类状态管理库,可以把余额存在全局状态里,充值操作完成后直接更新全局状态,所有订阅该状态的组件会自动刷新:
以Zustand为例:
// store.js import { create } from 'zustand'; export const useBalanceStore = create((set) => ({ balance: 0, updateBalance: (newBalance) => set({ balance: newBalance }), })); // 充值组件 function RechargeComponent() { const updateBalance = useBalanceStore((state) => state.updateBalance); const handleRecharge = async () => { // 调用充值接口 const response = await fetch('/api/recharge', { method: 'POST', body: ... }); const data = await response.json(); // 更新全局余额 updateBalance(data.newBalance); }; return <button onClick={handleRecharge}>充值</button>; } // 余额展示组件 function BalanceDisplay() { const balance = useBalanceStore((state) => state.balance); return <div>余额:{balance}</div>; }
3. 充值请求后即时更新
如果不需要全量实时,只是充值完成后同步,最简单的方式是在充值接口调用成功后,直接获取最新余额或者用接口返回的新值更新状态:
function BalanceComponent() { const [balance, setBalance] = useState(0); const handleRecharge = async () => { try { const rechargeRes = await fetch('/api/recharge', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ amount: 100 }) }); const rechargeData = await rechargeRes.json(); // 直接用接口返回的新余额更新 setBalance(rechargeData.newBalance); // 或者重新调用余额接口获取最新值 // const balanceRes = await fetch('/api/balance'); // const balanceData = await balanceRes.json(); // setBalance(balanceData.balance); } catch (err) { console.error('充值失败:', err); } }; return ( <div> <div>当前余额:{balance}</div> <button onClick={handleRecharge}>充值100元</button> </div> ); }
4. 优化版轮询(不推荐,但比setInterval好)
如果以上方案都无法实现,非要用轮询,可以用递归的setTimeout替代setInterval,避免请求堆积:
import { useEffect, useState } from 'react'; function BalanceComponent() { const [balance, setBalance] = useState(0); const fetchBalance = async () => { const res = await fetch('/api/balance'); const data = await res.json(); setBalance(data.balance); // 请求完成后再设置下一次轮询 setTimeout(fetchBalance, 5000); }; useEffect(() => { const timer = setTimeout(fetchBalance, 0); return () => clearTimeout(timer); }, []); return <div>当前余额:{balance}</div>; }
内容的提问来源于stack exchange,提问作者AX2
相关产品推荐
相关产品推荐

