使用Wagmi连接Solidity合约时遇TypeError与Hydration错误求助
解决Wagmi + NextJS的TypeError与Hydration错误
问题梳理
- 基于Wagmi连接Solidity合约,
_app.js已完成Provider配置,此前功能正常,当前出现两类错误:- 未添加try/catch时,
usePrepareContractWrite钩子抛出TypeError: Cannot read properties of null (reading 'id'),伴随Hydration相关错误 - 添加try/catch包裹后,触发
Hydration failed because the initial UI does not match what was rendered on the server错误
- 未添加try/catch时,
- 错误定位在
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的文件
验证步骤
- 重启NextJS开发服务器
- 切换到合约部署的链,连接钱包后测试交易按钮功能
- 检查控制台是否还有Hydration或TypeError报错
内容的提问来源于stack exchange,提问作者5efikkulin
相关产品推荐
相关产品推荐

