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

Next.js调用自定义Hook useUserSigner出现TypeError与无效Hook调用问题

错误原因

  1. 第一个useUserSigner is not a function报错:
    你的自定义Hook采用export default默认导出,最初使用import { useUserSigner }的命名导入语法无法获取到导出的函数,修改为默认导入语法后该问题解决。
  2. 第二个Invalid hook call报错:
    违反了React Hook的核心调用规则:Hook只能在函数组件的顶层作用域调用,不能在事件回调、异步函数、嵌套函数等非顶层位置调用。你将useUserSigner放在了点击事件触发的ConnectMetamask异步函数内执行,因此触发报错。

修复方案

不需要把Hook改成函数组件,只要调整Hook的调用位置即可:

  • 将useUserSigner的调用移到ConnectWallet组件的顶层作用域,Hook会自动监听传入的injectedProvider、localProvider参数变化,自动更新返回的userSigner值
  • 新增useEffect监听userSigner的变化,拿到有效值后执行后续业务逻辑
  • 可选优化:自定义Hook中你使用useMemo执行副作用逻辑不符合规范,建议替换为useEffect

修复后代码示例

src/pages/components/ConnectWallet.jsx

import { useState, useEffect } from "react";
import { ethers } from 'ethers'
import ConnectMetamaskButton from '../components/ConnectMetamaskButton';
// 保持默认导入写法
import useUserSigner from "../../hooks/UserSigner";
import detectEthereumProvider from '@metamask/detect-provider';

export default function ConnectWallet(props) {
    const [Accounts, setAccounts] = useState("Connect Metamask");
    const [injectedProvider, setInjectedProvider] = useState();
    const ethersProvider = ethers.getDefaultProvider("ropsten");
    // Hook调用移到组件顶层
    const userSigner = useUserSigner(injectedProvider, ethersProvider);

    // 监听userSigner变化,拿到值后执行后续逻辑
    useEffect(() => {
        if (userSigner) {
            console.log("userSigner = ", userSigner);
            // 此处补充拿到signer后的业务代码
        }
    }, [userSigner])

    async function ConnectMetamask() {
        try {
            const newAccounts = await ethereum.request({
                method: 'eth_requestAccounts',
            })
            setAccounts(newAccounts[0]);
        } catch (error) {
            console.error(error);
        }
        // 注意:你原代码中的getBalases未定义,请自行补充实现
        // getBalases();
        const provider = await detectEthereumProvider();
        setInjectedProvider(new ethers.providers.Web3Provider(provider));
    };

    return (
        <div>
            <header className="text-gray-100 bg-gray-900 body-font shadow w-full">
                <div className="container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center">
                    <nav className="flex lg:w-2/5 flex-wrap items-center text-base md:ml-auto">
                        <a className="mr-5 hover:text-gray-900 cursor-pointer border-b border-transparent hover:border-indigo-600"></a>
                    </nav>
                    <a
                        className="flex order-first lg:order-none lg:w-1/5 title-font font-medium items-center lg:items-center lg:justify-center mb-4 md:mb-0">
                        <span className="ml-3 text-xl">L2 DEX HUB</span>
                    </a>
                    <div className="lg:w-2/5 inline-flex lg:justify-end ml-5 lg:ml-0">
                        <ConnectMetamaskButton Accounts={Accounts} onClick={() => { ConnectMetamask(); }}></ConnectMetamaskButton>
                    </div>
                </div>
            </header>
        </div>
    );
};

可选优化:src/hooks/UserSigner.js

// 把useMemo替换为更适合执行副作用的useEffect
import { useEffect, useState } from "react";

const useUserSigner = (injectedProvider, localProvider) => {
  const [signer, setSigner] = useState();
  useEffect(() => {
    if (injectedProvider) {
      console.log("🦊 Using injected provider");
      const injectedSigner = injectedProvider._isProvider
        ? injectedProvider.getSigner()
        : injectedProvider;
      setSigner(injectedSigner);
    } else if (!localProvider) setSigner();
    else {
      if (
        window.location.pathname &&
        window.location.pathname.indexOf("/pk") >= 0
      ) {
        const incomingPK = window.location.hash.replace("#", "");
        let rawPK;
        if (incomingPK.length === 64 || incomingPK.length === 66) {
          console.log("🔑 Incoming Private Key...");
          rawPK = incomingPK;
          window.history.pushState({}, "", "/");
          const currentPrivateKey =
            window.localStorage.getItem("metaPrivateKey");
          if (currentPrivateKey && currentPrivateKey !== rawPK) {
            window.localStorage.setItem(
              "metaPrivateKey_backup" + Date.now(),
              currentPrivateKey
            );
          }
          window.localStorage.setItem("metaPrivateKey", rawPK);
        }
      }
    }
  }, [injectedProvider, localProvider]);
  return signer;
};
export default useUserSigner;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 13:21:02