英文:
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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论