You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js中Web3的getBalance与getBlock方法无响应问题

问题解决:Next.js中Web3自定义Hook获取区块无结果、getBalance无响应

核心问题拆解

  1. 自定义Hook违反React规则:React Hook不允许直接定义为async函数,必须同步执行。
  2. 异步逻辑时序错误:原代码用回调式getBlockNumber,循环内重复请求当前区块号,且异步操作未完成就返回空数组。
  3. 区块获取逻辑偏差:要获取前10个区块,需基于当前区块号递减获取,而非每次都请求最新区块。
  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 15:41:46