调用合约时,在哪里加载合约以防止未定义的方法?

Where to load contract to prevent undefined methods when calling contract?

我正在尝试 运行 来自我位于 ropsten 测试网络上的智能合约的方法,但是 运行 我的 getBalance() 函数中出现以下错误:

Unhandled Runtime Error TypeError: Cannot read properties of undefined (reading 'methods')

  46 |     async function getBalance() {
> 47 |       const tempBal = await window.contract.methods.balanceOf(account.data).call()
     |                                            ^
  48 |       setBalance(tempBal)
  49 |     }

连接我的合约:

export default function ConnectContract() {
    

    async function loadWeb3() {
        if (window.ethereum) {
          window.web3 = new Web3(window.ethereum)
          window.ethereum.enable()
        }
      }
  
      async function loadContract() {
        const tempContract = await new window.web3.eth.Contract(ABI, ContractAddress)
        return tempContract
      }
  
      async function load() {
        await loadWeb3();
        window.contract = await loadContract();
      }

      load();

}

目前调用的函数是这样的:

 export default function Page() {
    ConnectContract();
    getBalance();
 }

我之前调用它的另一种不会导致未定义错误的方法是在按钮内使用 onClick 函数来获取平衡:

 <div><button onClick={() => getBalance}>Get Balance</button></div>

我知道像这样调用它们会导致在合约连接到网站之前调用 getBalance 函数方法,但使用当前代码,它在刷新 1 页后工作。但是我还没有找到让合约加载的解决方案,所以该方法是在调用 getBalance 时定义的。

我已经尝试执行 onLoad 函数和 window.load() 函数但没有成功。我还尝试在 _app.js 中调用我的 ConnectContract 函数以在网站启动时加载它,但也没有成功。

您必须在 useEffect 钩子中调用 loadWeb3 函数。 useEffect 用于准备组件安装时的状态。要跟踪状态,请使用 useState hook

const [web3,setWeb3]=useState('')
const [contract,setContract]=useState('')

async function loadWeb3() {
        if (window.ethereum) {
          window.ethereum.enable()
          setWeb3(new Web3(window.ethereum))         
        }
      }

// [] array means we want this useEffect code run only when the compoenent rerenders
// Since loadWeb3 is async function, call it inside try/catch block
useEffect(()=>{loadWeb3()},[])

接下来我们需要按照同样的方式准备合同。

async function loadContract() {
        const tempContract = await new web3.eth.Contract(ABI, ContractAddress)
        setContract(tempContract)
      }

你也需要在useEffect中调用它,但这次它的依赖关系不同了。因为为了拿到合约,我们靠的是web3

// Since loadContract is async function, call it inside try/catch block
useEffect(()=>{loadContract},[web3])

当组件渲染时,首先 useEffect 将 运行 并设置 web3。由于 web3 已更改,组件将再次重新呈现此 useEffect 将 运行。因此,当您的组件被安装时,您将设置 web3contract 以便您可以使用它们。

您的问题出在这段代码中:

  46 |     async function getBalance() {
> 47 |       const tempBal = await window.contract.methods.balanceOf(account.data).call()
     |                                            ^
  48 |       setBalance(tempBal)
  49 |     }

我的猜测是 window.contract 要么不存在,要么在调用它之前没有加载。也许它要么在 window 不存在的服务器端位置被调用,要么尝试将其包装在 if 函数中,以便它在调用它之前检查以确保它存在