Ref 在 React 中的自定义钩子的不同调用之间重置。

huangapple go评论69阅读模式
英文:

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 &quot;ethers&quot;;
import { useRef } from &quot;react&quot;;

const useWallet = () =&gt; {
    const provider = new ethers.BrowserProvider(window.ethereum)
    const signer = useRef&lt;ethers.JsonRpcSigner&gt;();

    const connect = async () =&gt; {
        signer.current = await provider.getSigner();
        console.log(signer.current);
    };

    const deployContract = async (abi: InterfaceAbi, bytecode: string) =&gt; {
        console.log(signer.current);
        if (!signer.current) {
            throw new Error(&quot;Signer not connected&quot;);
        }

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

    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(() =&gt; {
        connect().catch((err) =&gt; {
            console.log(err)
        })
    }, [])

  return &lt;MyChildComponent /&gt;
}

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 &quot;react&quot;;

const WalletContext = createContext();

export default function WalletContextProvider({ children }) {
  const provider = new ethers.BrowserProvider(window.ethereum);
  const signer = useRef&lt;ethers.JsonRpcSigner&gt;();

  const connect = async () =&gt; {
    signer.current = await provider.getSigner();
    console.log(signer.current);
  };

  const deployContract = async (abi: InterfaceAbi, bytecode: string) =&gt; {
    console.log(signer.current);
    if (!signer.current) {
      throw new Error(&quot;Signer not connected&quot;);
    }

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

  return (
    &lt;WalletContext.Provider
      value={{
        connect,
        deployContract,
        signer,
      }}
    &gt;
      {children}
    &lt;/WalletContext.Provider&gt;
  );
}

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 &quot;./WalletContextProvider&quot;; // 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 &quot;./WalletContextProvider&quot;; // Make sure you use the correct path.

//...

&lt;WalletContextProvider&gt;
  &lt;App/&gt;
&lt;/WalletContextProvider&gt;

huangapple
  • 本文由 发表于 2023年5月7日 15:37:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76192697.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定