Solidity

使用 React.js 和 Next.js 連接到 Metamask,而無需每次都刷新頁面

  • November 19, 2021

StackShare 問題

您好,我在動態連接到 Metamask 時遇到問題。我使用 React.js 和 Next.js 創建了一個投票網站,並使用 UseEffect() 連接到 Metamask。目前,當我導航到帶有連結的頁面或對頁面進行任何更改時,我無法連接到我的智能合約,儘管我可以在手動刷新時連接。我相信這是由於 Next.js 預載入了數據。

例如,這是我在導航到 index.js 時看到的: 在此處輸入圖像描述

但是,在我刷新頁面後,一切通常都可以正常工作

在此處輸入圖像描述 這是我的 index.js 的基本實現

 useEffect(() => {// get web3
   async function initWeb3() {
     const web3Instance = await getWeb3();
     setWeb3(web3Instance)
   }
   initWeb3();
 },[]);

 useEffect(() => {// get Factory contract
   async function setup() {
     if(web3 == "") {
       console.log('unable to get factory')
       return;
     }
     try {
       // Get the contract instance.
       const networkId = await web3.eth.net.getId();
       const deployedNetwork = VoteFactoryContract.networks[networkId];
       const instance = new web3.eth.Contract(
         VoteFactoryContract.abi,
         deployedNetwork && deployedNetwork.address,
       );
       setContract(instance);// Set web3, accounts, and contract to the state, and then proceed with an
     } catch (error) {
       // Catch any errors for any of the above operations.
       alert(
         `Failed to load web3, accounts, or contract. Check console for details.`,
);
       console.error(error);
     }
   }
     setup();
 },[web3]);

 useEffect(()=> {//display available votes addresses
   var displayVotes = async () => {
     if(contract == ''){
       return;
     }
     const response = await contract.methods.getDeployedVotes().call();
     // Update state with the result.
     setVotesAddresses(response);
   };
   displayVotes();
 },[contract]);

getWeb3() 看起來像這樣:

import Web3 from "web3";

const getWeb3 = () =>
 new Promise((resolve, reject) => {
   // Wait for loading completion to avoid race conditions with web3 injection timing.
   window.addEventListener("load", async () => {
     // Modern dapp browsers...
     if (window.ethereum) {
       const web3 = new Web3(window.ethereum);
       try {
         // Request account access if needed
         await window.eth_requestAccounts;
         // Acccounts now exposed
         resolve(web3);
       } catch (error) {
         reject(error);
       }
     }
     // Legacy dapp browsers...
     else if (window.web3) {
       // Use Mist/MetaMask's provider.
       const web3 = window.web3;
       console.log("Injected web3 detected.");
       resolve(web3);
     }
     // Fallback to localhost; use dev console port by default...
     else {
       const provider = new Web3.providers.HttpProvider(
         "http://127.0.0.1:8545"
       );
       const web3 = new Web3(provider);
       console.log("No web3 instance injected, using Local web3.");
       resolve(web3);
     }
   });
 });

export default getWeb3;

最後,我的合約程式碼如下:

pragma solidity ^0.7.4;
//"SPDX-License-Identifier: UNLICENSED"

import "./Vote.sol";

contract VoteFactory{
   address[] public deployedVotes;
   
   function createVote(uint typeOf) public{
       address newVote = address(new Vote(msg.sender, typeOf));
       deployedVotes.push(newVote);
   }

   function getDeployedVotes() public view returns (address[] memory) {
       return deployedVotes;
   }
   
}

我花了無數個小時試圖調試它,雖然我覺得我知道錯誤是什麼,但我找不到解決方案。

這可能是因為您只在index.js;中建立 web3 連接。因此,當您導航到其他頁面時,它無法訪問。

您可以使用Redux從任何頁面訪問全域變數,或通過屬性在組件之間共享數據。

**更新: **

如果有幫助,這是我通常通過 React(在 Typescript 中)連接到 web3 的方式:

React.useEffect(() => {
   const init = async () => {
       await getWeb3(true);
   }
   init();
}, []);

const getWeb3 = async (isFirstLoad: boolean) => {
   try {
       let web3: Web3;
       if (window.ethereum) {
           web3 = new Web3(window.ethereum);
           // Ask User permission to connect to Metamask
           if (!isFirstLoad) {
               try {
                   await window.ethereum.enable();
               } catch (err) {
                   console.log('Transaction rejected by user:', err)
               };
           };
       } else if (window.web3) {
           web3 = new Web3(window.web3.currentProvider);
       } else {
           window.alert('Non-Ethereum browser detected. Please install MetaMask plugin');
           return;
       };

       dispatch(setWeb3(web3));  // Update web3 into Redux state

       // ...
   } catch (err) {
       console.log('Error in Web3.tsx -> getWeb3(): ', err);
   };
};

isFirstLoad如果他/她已經連接,該變數會阻止要求使用者重新連接到您的站點。

我在使用時遇到了一些問題window.addEventListener(),所以也許你可以嘗試不使用它。

引用自:https://ethereum.stackexchange.com/questions/93874