英文:
Ref resets between different calls of my custom hook in React
问题
I want to create a hook called useWallet to manage smart contract interaction:
import { ethers, InterfaceAbi, parseEther } from "ethers";
import { useRef } from "react";
const useWallet = () => {
    const provider = new ethers.BrowserProvider(window.ethereum);
    const signer = useRef<ethers.JsonRpcSigner>();
    const connect = async () => {
        signer.current = await provider.getSigner();
        console.log(signer.current);
    };
    const deployContract = async (abi: InterfaceAbi, bytecode: string) => {
        console.log(signer.current);
        if (!signer.current) {
            throw new Error("Signer not connected");
        }
        const factory = new ethers.ContractFactory(abi, bytecode, signer.current);
        await factory.deploy(parseEther("0"));
    };
    return {
        connect,
        deployContract,
        signer
    };
}
export default useWallet;
On my Home page, I call the connect method, and everything is OK (Metamask wallet opens):
export default function Home() {
    const { connect } = useWallet();
    useEffect(() => {
        connect().catch((err) => {
            console.log(err);
        });
    }, []);
  return <MyChildComponent />;
}
The problem is when I try to call the deployContract function (in MyChildComponent) the signer is always undefined.
It looks like at each useWallet call the signer is reset.
英文:
I want to create a hook called useWallet to manage smart contract interaction:
import {ethers, InterfaceAbi, parseEther} from "ethers";
import { useRef } from "react";
const useWallet = () => {
    const provider = new ethers.BrowserProvider(window.ethereum)
    const signer = useRef<ethers.JsonRpcSigner>();
    const connect = async () => {
        signer.current = await provider.getSigner();
        console.log(signer.current);
    };
    const deployContract = async (abi: InterfaceAbi, bytecode: string) => {
        console.log(signer.current);
        if (!signer.current) {
            throw new Error("Signer not connected");
        }
        const factory = new ethers.ContractFactory(abi, bytecode, signer.current)
        await factory.deploy(parseEther("0"))
    };
    return {
        connect,
        deployContract,
        signer
    };
}
export default useWallet;
On my Home page, I call the connect method, and everything is OK (Metamask wallet opens):
export default function Home() {
    const { connect } = useWallet()
    useEffect(() => {
        connect().catch((err) => {
            console.log(err)
        })
    }, [])
  return <MyChildComponent />
}
The problem is when I try to call the deployContract function (in MyChildComponent) the signer is always undefined.
It looks like at each useWallet call the signer is reset.
答案1
得分: 1
以下是翻译好的部分:
这是因为每次调用钩子或组件时,都会获得一个完全独立的实例。因此,signer.current 将与每个 useWallet 调用唯一绑定。
要拥有全局实例,我建议您使用上下文 API。例如,可以这样做:
// WalletContextProvider.js
import { createContext, useContext } from "react";
const WalletContext = createContext();
export default function WalletContextProvider({ children }) {
  const provider = new ethers.BrowserProvider(window.ethereum);
  const signer = useRef<ethers.JsonRpcSigner>();
  const connect = async () => {
    signer.current = await provider.getSigner();
    console.log(signer.current);
  };
  const deployContract = async (abi: InterfaceAbi, bytecode: string) => {
    console.log(signer.current);
    if (!signer.current) {
      throw new Error("Signer not connected");
    }
    const factory = new ethers.ContractFactory(abi, bytecode, signer.current);
    await factory.deploy(parseEther("0"));
  };
  return (
    <WalletContext.Provider
      value={{
        connect,
        deployContract,
        signer,
      }}
    >
      {children}
    </WalletContext.Provider>
  );
}
export function useWalletContext() {
  return useContext(WalletContext);
}
在需要钱包信息的任何地方,可以像下面这样获取它们。这样,您始终引用的是相同的实例:
import { useWalletContext } from "./WalletContextProvider"; // 确保使用正确的路径。
export default function SomeComponent() {
  const { connect } = useWalletContext();
  // ...
}
最后,确保在index.js等地方,通过 WalletContextProvider 包装您的组件:
import WalletContextProvider from "./WalletContextProvider"; // 确保使用正确的路径。
//...
<WalletContextProvider>
  <App/>
</WalletContextProvider>
英文:
This is because every time you call a hook or a component, you get a completely independent instance. So this signer.current will be uniquely tied to each useWallet call.
To have a global instance, I would suggest you use the Context API. As an example like so:
// WalletContextProvider.js
import { createContext, useContext } from "react";
const WalletContext = createContext();
export default function WalletContextProvider({ children }) {
  const provider = new ethers.BrowserProvider(window.ethereum);
  const signer = useRef<ethers.JsonRpcSigner>();
  const connect = async () => {
    signer.current = await provider.getSigner();
    console.log(signer.current);
  };
  const deployContract = async (abi: InterfaceAbi, bytecode: string) => {
    console.log(signer.current);
    if (!signer.current) {
      throw new Error("Signer not connected");
    }
    const factory = new ethers.ContractFactory(abi, bytecode, signer.current);
    await factory.deploy(parseEther("0"));
  };
  return (
    <WalletContext.Provider
      value={{
        connect,
        deployContract,
        signer,
      }}
    >
      {children}
    </WalletContext.Provider>
  );
}
export function useWalletContext() {
  return useContext(WalletContext);
}
Everywhere you need the wallet info, grab them like below. This way you are always referring to the same instance:
import { useWalletContext } from "./WalletContextProvider"; // Make sure you use the correct path.
export default function SomeComponent() {
  const { connect } = useWalletContext();
  // ...
}
Finally, ensure, for example, in index.js, you render your components wrapped by WalletContextProvider:
import WalletContextProvider from "./WalletContextProvider"; // Make sure you use the correct path.
//...
<WalletContextProvider>
  <App/>
</WalletContextProvider>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论