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

React自定义useWallet Hook中signer跨组件调用时重置为undefined问题

问题分析与解决方案

你的问题核心在于每次调用useWallet自定义Hook时,都会创建独立的useRef和provider实例。Home组件和MyChildComponent各自调用useWallet,它们的signer状态完全隔离——Home中通过connect赋值的signer只属于它自己的Hook实例,子组件的Hook里signer自然还是undefined。

要解决跨组件共享钱包状态的问题,需要用React Context来统一管理钱包相关的状态和方法,确保所有组件使用的是同一个状态实例。

具体实现步骤

1. 创建钱包Context与Provider

新建WalletContext.tsx文件,封装钱包状态和逻辑:

import { createContext, useContext, useState, useMemo } from 'react';
import { ethers, InterfaceAbi, parseEther } from "ethers";

// 定义Context类型
type WalletContextType = {
  signer: ethers.JsonRpcSigner | undefined;
  connect: () => Promise<void>;
  deployContract: (abi: InterfaceAbi, bytecode: string) => Promise<void>;
};

// 创建Context,默认值为undefined
const WalletContext = createContext<WalletContextType | undefined>(undefined);

// 定义Provider组件,统一管理钱包状态
export const WalletProvider = ({ children }: { children: React.ReactNode }) => {
  // 用useState存储signer,确保状态更新能触发组件重渲染
  const [signer, setSigner] = useState<ethers.JsonRpcSigner | undefined>(undefined);
  // 用useMemo缓存provider,避免每次渲染重复创建
  const provider = useMemo(() => new ethers.BrowserProvider(window.ethereum), []);

  const connect = async () => {
    const signerInstance = await provider.getSigner();
    setSigner(signerInstance);
    console.log(signerInstance);
  };

  const deployContract = async (abi: InterfaceAbi, bytecode: string) => {
    if (!signer) {
      throw new Error("Signer not connected");
    }

    const factory = new ethers.ContractFactory(abi, bytecode, signer);
    await factory.deploy(parseEther("0"));
  };

  return (
    <WalletContext.Provider value={{ signer, connect, deployContract }}>
      {children}
    </WalletContext.Provider>
  );
};

// 封装自定义Hook,简化Context调用
export const useWallet = () => {
  const context = useContext(WalletContext);
  if (!context) {
    throw new Error('useWallet must be used within a WalletProvider');
  }
  return context;
};

2. 在根组件中包裹Provider

修改你的根组件(如App.tsx),用WalletProvider包裹所有需要使用钱包功能的组件:

import { WalletProvider } from './WalletContext';
import Home from './Home';

function App() {
  return (
    <WalletProvider>
      <Home />
    </WalletProvider>
  );
}

export default App;

3. 修改Home组件使用统一的Hook

import { useEffect } from 'react';
import { useWallet } from './WalletContext';
import MyChildComponent from './MyChildComponent';

export default function Home() {
  const { connect } = useWallet();

  useEffect(() => {
    connect().catch((err) => {
      console.log(err);
    });
  }, [connect]); // 依赖connect,确保函数更新时重新执行

  return <MyChildComponent />;
}

4. 在子组件中调用deployContract

import { useWallet } from './WalletContext';

export default function MyChildComponent() {
  const { deployContract } = useWallet();

  const handleDeploy = async () => {
    // 替换为你的合约ABI和字节码
    const contractAbi = [];
    const contractBytecode = "";
    
    try {
      await deployContract(contractAbi, contractBytecode);
      console.log("合约部署成功");
    } catch (err) {
      console.error("部署失败:", err);
    }
  };

  return (
    <button onClick={handleDeploy}>部署智能合约</button>
  );
}

关键优化点

  • 使用useState替代useRef存储signer:useState的状态更新会触发组件重渲染,确保子组件能同步拿到最新的signer。
  • 用useMemo缓存provider:避免组件每次渲染都创建新的BrowserProvider实例,提升性能。
  • 通过Context共享状态:所有调用useWallet的组件都会拿到同一个状态实例,解决了状态隔离的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 01:09:56