Solidity
使用 React.js 和 Next.js 連接到 Metamask,而無需每次都刷新頁面
StackShare 問題
您好,我在動態連接到 Metamask 時遇到問題。我使用 React.js 和 Next.js 創建了一個投票網站,並使用 UseEffect() 連接到 Metamask。目前,當我導航到帶有連結的頁面或對頁面進行任何更改時,我無法連接到我的智能合約,儘管我可以在手動刷新時連接。我相信這是由於 Next.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()
,所以也許你可以嘗試不使用它。