如何使用 Web3-react 保持 MetaMask 与 UI 的持久连接?
How to keep MetaMask connection to the UI persistent with Web3-react?
我正在使用 web3-react,我不知道如何在浏览器刷新时保持与 MetaMask 钱包的连接。
这是代码:
// define the injectedConnectors
const injectedConnector = new InjectedConnector({
supportedChainIds: [
1, // Mainet
3, // Ropsten
4, // Rinkeby
5, // Goerli
42, // Kovan
],
})
const { chainId, account, activate, active } = useWeb3React()
// activate the wallet
activate(injectedConnector)
console.log(account)
// all good.
到目前为止,一切正常,我激活了我的 MetaMask 钱包,并且我正确地记录了帐户,并且 active 变量是一个布尔值,它会变为 true。
问题是,当我刷新页面时,active 变为 false,我失去了 UI 与 MetaMask 钱包之间的连接。当然,将 active 保存到浏览器中不会改变任何内容,因为连接依赖于 active 布尔值。
文档缺少此类信息。
任何帮助将不胜感激。
终于找到解决办法了!
我试图使用官方库中的示例使用 ... 但由于某种原因它没有工作,尽管没有出现错误。
然后我偶然发现了一个遇到同样问题的人,然后 post 在 reddit 上编辑并得到了一个适合我的好答案。
这是 link 到 post:https://www.reddit.com/r/ethdev/comments/nw7iyv/displaying_connected_wallet_after_browser_refresh/h5uxl88/?context=3
这是 post:
中的代码
首先创建一个包含名为 connectors.js:
的注入连接器的文件
import { InjectedConnector } from '@web3-react/injected-connector'
export const Injected = new InjectedConnector({ supportedNetworks: [1, 3, 4, 5, 42] })
然后创建一个组件来检查用户是否已经激活了钱包:
import React, { useEffect, useState } from 'react'
import { injected } from '../connectors'
import { useWeb3React } from '@web3-react/core'
function MetamaskProvider({ children }) {
const { active: networkActive, error: networkError, activate: activateNetwork } = useWeb3React()
const [loaded, setLoaded] = useState(false)
useEffect(() => {
injected
.isAuthorized()
.then((isAuthorized) => {
setLoaded(true)
if (isAuthorized && !networkActive && !networkError) {
activateNetwork(injected)
}
})
.catch(() => {
setLoaded(true)
})
}, [activateNetwork, networkActive, networkError])
if (loaded) {
return children
}
return <>Loading</>
}
export default MetamaskProvider
然后将 MetamaskProvider 包裹在您希望钱包在刷新时激活的组件周围:
return (
<ThemeProvider theme={darkMode ? darkTheme : lightTheme}>
<StylesProvider injectFirst>
<Paper>
<Router>
<Web3ReactProvider getLibrary={getLibrary}>
<MetamaskProvider>
{...children components}
</MetamaskProvider>
</Web3ReactProvider>
</Router>
</Paper>
</StylesProvider>
</ThemeProvider>
);
其实很简单。您可以只将连接地址存储在本地存储中,当用户单击断开连接按钮时,然后从本地存储中删除该地址。基本上我们使用的条件是,如果本地存储中有一个帐户,那么我们在加载时连接,如果没有,那么我们必须手动单击连接按钮。考虑下面的代码。请注意,理想情况下,您应该将逻辑编写为钩子并在主应用程序中使用钩子,然后将 useWeb3React() 返回的“活动”状态传递给道具。但为了这个例子的目的,我只是将连接逻辑放在一个文件中以使其更容易阅读
import React, { useState, useEffect } from 'react';
import Web3 from 'web3';
import detectEthereumProvider from '@metamask/detect-provider';
import { useWeb3React } from "@web3-react/core"
import { InjectedConnector } from '@web3-react/injected-connector'
//declare supportated chains
export const injected = new InjectedConnector({
supportedChainIds: [1, 3, 4, 5, 42, 1337, 43114],
})
export default function connButton() {
var web3;
var accounts;
var connected
const [loading, setLoading] = useState(false)
//here we can destructure out various things from web3React such as
//active (which is true if the user is connected and false otherwise)
//activate and deactiveate which we use to instansiate and break the users
//connection
const { active, account, library, connector, activate, deactivate } = useWeb3React()
//set up an elemnt in local storage that we use to hold the connected account
var acc = localStorage.getItem("account")
//function that initialises web3.js
const connectWalletHandler = () => {
if (window.ethereum && window.ethereum.isMetaMask) {
console.log('MetaMask Here!');
web3 = new Web3(window.ethereum);
window.ethereum.request({ method: 'eth_requestAccounts'})
} else {
console.log('Need to install MetaMask');
// setErrorMessage('Please install MetaMask browser extension to interact');
}
console.log(web3.eth.currentProvider)
}
//function that is called on page load if and only if their exists and
//item for the user accoun tin local storage
async function connectOnLoad() {
try {
//here we use activate to create the connection
await activate(injected)
connected = true
} catch (ex) {
console.log(ex)
}
//we use web3.eth to get the accounts to store it in local storage
var accounts1 = await web3.eth.getAccounts();
acc = localStorage.setItem("account", accounts1);
}
//here we use a useEffect so that on page load we can check if there is
//an account in local storage. if there is we call the connect onLoad func
//above which allows us to presist the connection and i also call connectWalletHandler
which sets up web3.js so we can call web3.eth.getAccounts()
useEffect(() => {
if (acc != null) {
connectOnLoad()
}
connectWalletHandler()
}, [])
//however in the case where there is no item in local storage we use this
//function to connect which is called when we click the connect button. its
//essentially the same but we check if local storage is null if it is we activate
//if its not then we disconnect. And when we disconnect we remove the acccount from local storage
async function connectOnClick() {
if (localStorage.getItem("account") == null) {
setLoading(true);
try {
await activate(injected)
connected = true
} catch (ex) {
console.log(ex)
}
// window.location.reload();
var accounts1 = await web3.eth.getAccounts();
console.log(accounts1)
acc = localStorage.setItem("account", accounts1);
console.log(acc)
setTimeout(function(){
setLoading(false)
}, 1600);//wait 2 seconds
} else {
disconnect();
connected = false
}
}
async function disconnect() {
try {
deactivate()
localStorage.removeItem("account");
} catch (ex) {
console.log(ex)
}
}
return (
//remember the active boolean from useReactWeb3() stores a bool
//depending on if the user is or is not connected there for we can
//use this as a conditon to render the button saying "Connect Wallet"
or displaying their address as the text.
<div>
{active ? <button onClick={connectOnClick}>{account.substring(0, 6)}...{account.substring(account.length - 4)}</button> : <button onClick={connectOnClick}>Connect Wallet</button>}
</div>
);
}
然后在您的 app.js 中记得将您的整个应用包裹在标签中。请记住,这意味着您需要将 web3React 导入 app.js 以及
我正在使用 web3-react,我不知道如何在浏览器刷新时保持与 MetaMask 钱包的连接。
这是代码:
// define the injectedConnectors
const injectedConnector = new InjectedConnector({
supportedChainIds: [
1, // Mainet
3, // Ropsten
4, // Rinkeby
5, // Goerli
42, // Kovan
],
})
const { chainId, account, activate, active } = useWeb3React()
// activate the wallet
activate(injectedConnector)
console.log(account)
// all good.
到目前为止,一切正常,我激活了我的 MetaMask 钱包,并且我正确地记录了帐户,并且 active 变量是一个布尔值,它会变为 true。 问题是,当我刷新页面时,active 变为 false,我失去了 UI 与 MetaMask 钱包之间的连接。当然,将 active 保存到浏览器中不会改变任何内容,因为连接依赖于 active 布尔值。 文档缺少此类信息。 任何帮助将不胜感激。
终于找到解决办法了! 我试图使用官方库中的示例使用 ... 但由于某种原因它没有工作,尽管没有出现错误。 然后我偶然发现了一个遇到同样问题的人,然后 post 在 reddit 上编辑并得到了一个适合我的好答案。 这是 link 到 post:https://www.reddit.com/r/ethdev/comments/nw7iyv/displaying_connected_wallet_after_browser_refresh/h5uxl88/?context=3 这是 post:
中的代码首先创建一个包含名为 connectors.js:
的注入连接器的文件import { InjectedConnector } from '@web3-react/injected-connector'
export const Injected = new InjectedConnector({ supportedNetworks: [1, 3, 4, 5, 42] })
然后创建一个组件来检查用户是否已经激活了钱包:
import React, { useEffect, useState } from 'react'
import { injected } from '../connectors'
import { useWeb3React } from '@web3-react/core'
function MetamaskProvider({ children }) {
const { active: networkActive, error: networkError, activate: activateNetwork } = useWeb3React()
const [loaded, setLoaded] = useState(false)
useEffect(() => {
injected
.isAuthorized()
.then((isAuthorized) => {
setLoaded(true)
if (isAuthorized && !networkActive && !networkError) {
activateNetwork(injected)
}
})
.catch(() => {
setLoaded(true)
})
}, [activateNetwork, networkActive, networkError])
if (loaded) {
return children
}
return <>Loading</>
}
export default MetamaskProvider
然后将 MetamaskProvider 包裹在您希望钱包在刷新时激活的组件周围:
return (
<ThemeProvider theme={darkMode ? darkTheme : lightTheme}>
<StylesProvider injectFirst>
<Paper>
<Router>
<Web3ReactProvider getLibrary={getLibrary}>
<MetamaskProvider>
{...children components}
</MetamaskProvider>
</Web3ReactProvider>
</Router>
</Paper>
</StylesProvider>
</ThemeProvider>
);
其实很简单。您可以只将连接地址存储在本地存储中,当用户单击断开连接按钮时,然后从本地存储中删除该地址。基本上我们使用的条件是,如果本地存储中有一个帐户,那么我们在加载时连接,如果没有,那么我们必须手动单击连接按钮。考虑下面的代码。请注意,理想情况下,您应该将逻辑编写为钩子并在主应用程序中使用钩子,然后将 useWeb3React() 返回的“活动”状态传递给道具。但为了这个例子的目的,我只是将连接逻辑放在一个文件中以使其更容易阅读
import React, { useState, useEffect } from 'react';
import Web3 from 'web3';
import detectEthereumProvider from '@metamask/detect-provider';
import { useWeb3React } from "@web3-react/core"
import { InjectedConnector } from '@web3-react/injected-connector'
//declare supportated chains
export const injected = new InjectedConnector({
supportedChainIds: [1, 3, 4, 5, 42, 1337, 43114],
})
export default function connButton() {
var web3;
var accounts;
var connected
const [loading, setLoading] = useState(false)
//here we can destructure out various things from web3React such as
//active (which is true if the user is connected and false otherwise)
//activate and deactiveate which we use to instansiate and break the users
//connection
const { active, account, library, connector, activate, deactivate } = useWeb3React()
//set up an elemnt in local storage that we use to hold the connected account
var acc = localStorage.getItem("account")
//function that initialises web3.js
const connectWalletHandler = () => {
if (window.ethereum && window.ethereum.isMetaMask) {
console.log('MetaMask Here!');
web3 = new Web3(window.ethereum);
window.ethereum.request({ method: 'eth_requestAccounts'})
} else {
console.log('Need to install MetaMask');
// setErrorMessage('Please install MetaMask browser extension to interact');
}
console.log(web3.eth.currentProvider)
}
//function that is called on page load if and only if their exists and
//item for the user accoun tin local storage
async function connectOnLoad() {
try {
//here we use activate to create the connection
await activate(injected)
connected = true
} catch (ex) {
console.log(ex)
}
//we use web3.eth to get the accounts to store it in local storage
var accounts1 = await web3.eth.getAccounts();
acc = localStorage.setItem("account", accounts1);
}
//here we use a useEffect so that on page load we can check if there is
//an account in local storage. if there is we call the connect onLoad func
//above which allows us to presist the connection and i also call connectWalletHandler
which sets up web3.js so we can call web3.eth.getAccounts()
useEffect(() => {
if (acc != null) {
connectOnLoad()
}
connectWalletHandler()
}, [])
//however in the case where there is no item in local storage we use this
//function to connect which is called when we click the connect button. its
//essentially the same but we check if local storage is null if it is we activate
//if its not then we disconnect. And when we disconnect we remove the acccount from local storage
async function connectOnClick() {
if (localStorage.getItem("account") == null) {
setLoading(true);
try {
await activate(injected)
connected = true
} catch (ex) {
console.log(ex)
}
// window.location.reload();
var accounts1 = await web3.eth.getAccounts();
console.log(accounts1)
acc = localStorage.setItem("account", accounts1);
console.log(acc)
setTimeout(function(){
setLoading(false)
}, 1600);//wait 2 seconds
} else {
disconnect();
connected = false
}
}
async function disconnect() {
try {
deactivate()
localStorage.removeItem("account");
} catch (ex) {
console.log(ex)
}
}
return (
//remember the active boolean from useReactWeb3() stores a bool
//depending on if the user is or is not connected there for we can
//use this as a conditon to render the button saying "Connect Wallet"
or displaying their address as the text.
<div>
{active ? <button onClick={connectOnClick}>{account.substring(0, 6)}...{account.substring(account.length - 4)}</button> : <button onClick={connectOnClick}>Connect Wallet</button>}
</div>
);
}
然后在您的 app.js 中记得将您的整个应用包裹在标签中。请记住,这意味着您需要将 web3React 导入 app.js 以及