Next.js中Web3的getBalance与getBlock方法无响应问题
问题解决:Next.js中Web3自定义Hook获取区块无结果、getBalance无响应
核心问题拆解
- 自定义Hook违反React规则:React Hook不允许直接定义为
async函数,必须同步执行。 - 异步逻辑时序错误:原代码用回调式
getBlockNumber,循环内重复请求当前区块号,且异步操作未完成就返回空数组。 - 区块获取逻辑偏差:要获取前10个区块,需基于当前区块号递减获取,而非每次都请求最新区块。
- Web3实例初始化问题:Hook内重复创建Web3实例,且未处理Next.js服务端渲染时
window.ethereum不存在的场景。
修复后的代码实现
1. 修正useBlock Hook
把异步逻辑封装在Hook内部的useEffect中,用Promise处理时序,正确获取前10个区块:
import { useState, useEffect } from 'react'; import Web3 from 'web3'; import type { BlockTransactionString } from 'web3-eth'; export const useBlock = () => { const [blocks, setBlocks] = useState<BlockTransactionString[]>([]); const [error, setError] = useState<string | null>(null); useEffect(() => { const fetchBlocks = async () => { try { // 兼容Next.js服务端渲染,确保在浏览器环境初始化Web3 if (typeof window === 'undefined' || !window.ethereum) { throw new Error('Web3钱包未连接或环境不支持'); } const web3 = new Web3(window.ethereum); // 获取当前最新区块号 const currentBlockNum = await web3.eth.getBlockNumber(); const fetchedBlocks: BlockTransactionString[] = []; // 循环获取前10个区块(当前区块 - 0 到 -9) for (let i = 0; i < 10; i++) { const block = await web3.eth.getBlock(currentBlockNum - i); block && fetchedBlocks.push(block); } setBlocks(fetchedBlocks); } catch (err) { setError(err instanceof Error ? err.message : '获取区块失败'); console.error(err); } }; fetchBlocks(); }, []); return { blocks, error }; };
2. 修正Blocks组件
直接使用修正后的Hook获取状态,添加加载和错误提示:
import React from 'react'; import { useBlock } from '../hooks/useBlock'; import Block from './Block'; const Blocks = () => { const { blocks, error } = useBlock(); if (error) { return <div>错误:{error}</div>; } if (blocks.length === 0) { return <div>加载区块中...</div>; } return ( <div> {blocks.map((block) => ( <Block key={block.hash} block={block} /> ))} </div> ); }; export default Blocks;
3. Block组件类型优化(可选)
把any类型替换为明确的Web3区块类型,同时优化时间戳显示:
import React from "react"; import { Card } from "react-bootstrap"; import type { BlockTransactionString } from 'web3-eth'; interface BlockProps { block: BlockTransactionString; } const Block: React.FC<BlockProps> = ({ block }) => { return ( <Card className="p-4"> <p className="fs-6">区块编号:{block.number}</p> <p className="fs-6">区块时间:{new Date(block.timestamp * 1000).toLocaleString()}</p> <p className="fs-6">区块哈希:{block.hash}</p> <p className="fs-6">父区块哈希:{block.parentHash}</p> <p className="fs-6">Nonce:{block.nonce.toString()}</p> <p className="fs-6">难度值:{block.difficulty.toString()}</p> </Card> ); }; export default Block;
web3.eth.getBalance()无响应的解决思路
同样用async/await替代回调,确保异步操作等待完成:
// 获取地址余额示例 const fetchBalance = async (address: string) => { if (!window.ethereum) return null; const web3 = new Web3(window.ethereum); try { const balanceWei = await web3.eth.getBalance(address); // 转换为ETH单位 return web3.utils.fromWei(balanceWei, 'ether'); } catch (err) { console.error('获取余额失败:', err); return null; } };
关键注意事项
- 严格遵循React Hook规则:自定义Hook不能是
async函数,异步逻辑必须放在useEffect内部的async函数中。 - 优先使用
async/await:避免Web3回调嵌套导致的时序混乱,确保所有异步操作完成后再更新状态。 - 兼容Next.js SSR:必须检查
window对象是否存在,防止服务端渲染时出现报错。 - 区块获取逻辑:要获取历史区块,需基于当前区块号递减,而非重复请求最新区块。
内容的提问来源于stack exchange,提问作者Caleb Kutz
相关产品推荐
相关产品推荐

