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

使用Wagmi连接Solidity合约时遇TypeError与Hydration错误求助

解决Wagmi + NextJS的TypeError与Hydration错误

问题梳理

  • 基于Wagmi连接Solidity合约,_app.js已完成Provider配置,此前功能正常,当前出现两类错误:
    1. 未添加try/catch时,usePrepareContractWrite钩子抛出TypeError: Cannot read properties of null (reading 'id'),伴随Hydration相关错误
    2. 添加try/catch包裹后,触发Hydration failed because the initial UI does not match what was rendered on the server错误
  • 错误定位在Header.jsx,已通过Hardhat成功导出合约ABI与地址到项目中

解决方案

1. 修复usePrepareContractWrite的空值与链ID问题

服务端渲染阶段,Wagmi尚未获取钱包链信息,导致合约配置为空触发读取错误。需明确指定链ID,并添加钩子启用条件:

// Header.jsx 中修改usePrepareContractWrite逻辑
import { useAccount } from "wagmi";

export default function Header() {
  const { disconnect } = useDisconnect();
  const { connector, address, isConnected, chain } = useAccount(); // 新增chain字段
  const { connect, connectors, error, pendingConnector } = useConnect();

  const TARGET_CHAIN_ID = 1; // 替换为你的合约部署链ID(如Goerli为5)
  const contractAddress = contractAddresses[TARGET_CHAIN_ID]; // 从常量文件取对应链的地址

  const { config } = usePrepareContractWrite({
    address: contractAddress,
    abi: contractAbi,
    functionName: "functionName",
    chainId: TARGET_CHAIN_ID, // 指定合约链ID
    enabled: !!chain && chain.id === TARGET_CHAIN_ID, // 仅链匹配时启用钩子
  });
  const { data: functionName, write } = useContractWrite(config);
  
  // 其余代码保持不变
}

2. 消除Hydration不匹配

服务端渲染无钱包状态,客户端渲染时状态变化导致前后UI不一致,可通过两种方式解决:

方案一:延迟客户端渲染

使用useEffect标记客户端就绪状态,仅在客户端挂载后渲染依赖钱包的UI:

// Header.jsx 中添加状态与副作用
import { useState, useEffect } from "react";

export default function Header() {
  const [isClientReady, setIsClientReady] = useState(false);

  useEffect(() => {
    setIsClientReady(true); // 客户端挂载后触发
  }, []);

  // 其余钩子逻辑保持不变

  return (
    <>
      {isClientReady ? (
        <>
          {connector ? (
            // 已连接状态的UI
            <div className={styles.connectedTo}>...</div>
          ) : (
            // 未连接状态的UI
            <div className={styles.dropdown}>...</div>
          )}

          <button
            className={`${styles2.btn} ${styles.heroBtn}`}
            disabled={!write || isLoading || (!chain || chain.id !== TARGET_CHAIN_ID)}
            onClick={() => write?.()}
          >
            {isLoading ? "Pending..." : "Execute transaction"}
          </button>
        </>
      ) : (
        // 服务端渲染时的占位UI(可自定义)
        <div className={styles.loading}></div>
      )}
    </>
  );
}

方案二:禁用组件SSR

如果Header完全依赖钱包状态,直接通过NextJS动态导入禁用服务端渲染:

// 在使用Header的页面文件中(如pages/index.js)
import dynamic from "next/dynamic";

const Header = dynamic(() => import("../components/Header"), {
  ssr: false,
});

// 正常使用Header组件
export default function Home() {
  return <Header />;
}

3. 修正Hardhat导出脚本路径错误

原脚本中地址与ABI文件路径写反,导致读取错误,修正如下:

// Hardhat导出脚本
const FRONT_END_ADDRESSES_FILE = "/PATH/contractAdress.json"; // 存储地址的文件
const FRONT_END_ABI_FILE = "/PATH/contractABI.json"; // 存储ABI的文件

验证步骤

  1. 重启NextJS开发服务器
  2. 切换到合约部署的链,连接钱包后测试交易按钮功能
  3. 检查控制台是否还有Hydration或TypeError报错

内容的提问来源于stack exchange,提问作者5efikkulin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 22:29:58