React 路由器组件多次调用函数
React router component calls function multiple times
我正在尝试学习区块链开发,所以我几周前开始学习 Solidity,我不得不为我用 React 制定的合约创建一个前端应用程序,我也不知道。
因此,我阅读了文档并观看了使用 web3 库和一些页面转换的教程。现在我可以在我的应用程序中的页面之间导航,但是当我路由到特定页面时,我的函数会被多次调用。
这是我的 index.js 每次我 运行 应用程序时都会加载它。我已经这样设置了我的路线。
index.js(我没有app.js,像app.js一样使用index.js)
import React from 'react';
import ReactDOM from 'react-dom';
import './style.css';
import Web3 from 'web3'
import { ConnectPage } from './ConnectPage';
import { MintPage } from './MintPage';
import { AllCryptonauts } from './AllCryptonauts';
import { MyCryptonauts } from './MyCryptonauts';
import { NotFound } from './NotFound';
import { BrowserRouter, Route, Switch } from 'react-router-dom'
function App() {
if (window.ethereum) {
window.web3 = new Web3(window.ethereum)
window.ethereum.enable()
} else {
alert("Could not detect MetaMask. Please install MetaMask before proceeding!");
}
return (
<div>
<Switch>
<Route exact path="/" component={ConnectPage} />
<Route exact path="/MintPage" component={MintPage} />
<Route exact path="/MyCryptonauts" component={MyCryptonauts} />
<Route exact path="/AllCryptonauts" component={AllCryptonauts} />
<Route path="*" component={NotFound} />
</Switch>
</div>
);
}
ReactDOM.render(
<React.StrictMode>
<BrowserRouter><App /></BrowserRouter>
</React.StrictMode>,
document.getElementById("root")
);
我在第一页创建了一个连接按钮,它将我重定向到 mint 部分。
ConnectPage.js
import React from 'react'
export const ConnectPage = (props) => {
async function Connect() {
const web3 = window.web3
const networkId = await web3.eth.net.getId()
if (networkId === 4) {
props.history.push("/MintPage")
} else {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: '0x4' }],
});
props.history.push("/MintPage")
}
}
return (
<div>
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}><img src="https://nftornek.000webhostapp.com/frontend/cnlogo.png"></img></div>
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', marginTop: '5%'}}><button className="connectButton" onClick={Connect}>Enter the Universe</button></div>
</div>
)
}
这是我进行铸币交易的地方。我已经输入 console.log("called checkChainID") 来查看 checkChainID 函数被调用了多少次。每次加载页面时都会调用 12 次,在我尝试导航到同一页面后调用两次。
作为所有这些方面的初学者,我收集了我从教程中获得的信息,这些信息不是很明显,并尝试制作一个带有路由的测试应用程序(我也按照教程做了)
路由的工作原理与我在教程中所做的完全一样,我想继续为我的应用程序使用这个路由示例,但可能由于我缺乏 React 的基础知识,我在页面中做错了.我已经研究这个问题好几个小时了,但我真的不明白我能做些什么来解决它。
我认为是因为 useState,因为它会在我每次调用它时呈现应用程序,但我不确定是否有任何方法可以防止这种情况发生,或者想出更聪明的方法。
MintPage.js
import React, { useState } from 'react';
import Web3 from 'web3'
import Contract from "../src/build/Contract.json";
export const MintPage = (props) => {
const web3 = window.web3;
var [currentAccount, setCurrentAccount] = useState("0x0");
var [currentBalance, setCurrentBalance] = useState("0");
var [mintAmount, setMintAmount] = useState(1);
const [addAmount, setAddAmount] = useState(true);
const [subtractAmount, setSubtractAmount] = useState(false);
window.ethereum.on('chainChanged', (_chainId) => checkChainID());
window.ethereum.on('accountsChanged', (_accounts) => loadBlockchainData());
checkChainID();
async function checkChainID() {
const networkId = await web3.eth.net.getId();
if (networkId !== 4) {
props.history.push("/")
} else {
loadBlockchainData();
}
console.log("called checkChainID")
}
async function loadBlockchainData() {
window.web3 = new Web3(window.ethereum);
const accounts = await web3.eth.getAccounts();
setCurrentAccount(accounts[0]);
getBalance(accounts[0]);
}
async function getBalance(acc) {
const balance = await web3.eth.getBalance(acc);
var balanceEth = web3.utils.fromWei(balance, 'ether');
setCurrentBalance(parseFloat(balanceEth).toFixed(3) + " ETH");
const SmartContractObj = new web3.eth.Contract(Contract.abi, "0x187FF2d65dd7204f11ea0487F2EED36378946902");
}
function MintPage() {
props.history.push("/MintPage")
}
function MyCryptonauts() {
props.history.push("/MyCryptonauts")
}
function AllCryptonauts() {
props.history.push("/AllCryptonauts")
}
function Disconnect() {
props.history.push("/")
}
return (
<div>
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}><img src="https://nftornek.000webhostapp.com/frontend/cnlogo.png" width='500' height='180'></img></div>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<button className="regularButton divide" onClick={MintPage}>Mint</button>
<button className="regularButton divide" onClick={MyCryptonauts}>My Cryptonauts</button>
<button className="regularButton divide" onClick={AllCryptonauts}>All Cryptonauts</button>
<button className="regularButton divide" onClick={Disconnect}>Disconnect</button>
</div>
<div style={{ display: 'flex', justifyContent: 'center' }}><p className="accountText">Current Account: {currentAccount}</p></div>
<div style={{ marginTop: '25%' }}></div>
<div style={{ display: 'flex', justifyContent: 'center' }}><p className="accountText">Mint {mintAmount} Cryptonaut for XXX ETH</p></div>
<div style={{ display: 'flex', justifyContent: 'center' }}><button className="amountButton divide" disabled={subtractAmount ? 0 : 1} onClick={() => {
if (mintAmount <= 1) {
setMintAmount(1);
setSubtractAmount(false);
} else {
setMintAmount(mintAmount - 1);
} if (mintAmount === 2) {
setSubtractAmount(false);
} if (mintAmount >= 1) {
setAddAmount(true);
}
}}>-
</button>
<button className="mintButton divide" onClick={() => {
console.log(mintAmount);
}}>MINT
</button>
<button className="amountButton divide" disabled={addAmount ? 0 : 1} onClick={() => {
if (mintAmount >= 5) {
setMintAmount(5);
} else {
setMintAmount(mintAmount + 1);
}
if (mintAmount === 4) {
setAddAmount(false);
} if (mintAmount >= 1) {
setSubtractAmount(true);
}
}}>+
</button>
</div>
<div style={{ display: 'flex', justifyContent: 'center', marginTop: '7px' }}><p className="accountText">Current Balance: {currentBalance}</p></div>
</div>
)
}
如有任何帮助,我将不胜感激。请指出正确的方向!不过,可能需要更详细的解释,因为我对此太陌生了。 :) 谢谢大家。
有问题的是这三行:
window.ethereum.on('chainChanged', (_chainId) => checkChainID());
window.ethereum.on('accountsChanged', (_accounts) => loadBlockchainData());
checkChainID();
您通常不希望在组件的渲染周期中直接调用函数,因为组件可以出于多种不同的原因重新渲染。每次此组件呈现时,它都会向全局 ethereum
对象添加一个事件侦听器,因此您将在每个组件呈现时获得一个额外的侦听器。由于这是一个功能组件,您应该将这些行包装在一个效果挂钩中,以便您可以控制它们何时 运行.
useEffect(() => {
window.ethereum.on('chainChanged', (_chainId) => checkChainID());
window.ethereum.on('accountsChanged', (_accounts) => loadBlockchainData());
checkChainID();
return () => {
// remove the 'chainChanged' and 'accountsChanged' event listeners
}
}, [])
空数组告诉此效果仅 运行 一次,即组件首次呈现时。如果你需要它 运行 当一些状态改变时,或者你需要访问函数中的任何状态变量 运行 在效果中,你需要列出依赖数组中的那些。
您还需要 return 从钩子中移除监听器的函数,这样他们就不会在每次用户访问页面、离开和 returns.
您可以阅读更多关于 useEffect
here。
我正在尝试学习区块链开发,所以我几周前开始学习 Solidity,我不得不为我用 React 制定的合约创建一个前端应用程序,我也不知道。
因此,我阅读了文档并观看了使用 web3 库和一些页面转换的教程。现在我可以在我的应用程序中的页面之间导航,但是当我路由到特定页面时,我的函数会被多次调用。
这是我的 index.js 每次我 运行 应用程序时都会加载它。我已经这样设置了我的路线。
index.js(我没有app.js,像app.js一样使用index.js)
import React from 'react';
import ReactDOM from 'react-dom';
import './style.css';
import Web3 from 'web3'
import { ConnectPage } from './ConnectPage';
import { MintPage } from './MintPage';
import { AllCryptonauts } from './AllCryptonauts';
import { MyCryptonauts } from './MyCryptonauts';
import { NotFound } from './NotFound';
import { BrowserRouter, Route, Switch } from 'react-router-dom'
function App() {
if (window.ethereum) {
window.web3 = new Web3(window.ethereum)
window.ethereum.enable()
} else {
alert("Could not detect MetaMask. Please install MetaMask before proceeding!");
}
return (
<div>
<Switch>
<Route exact path="/" component={ConnectPage} />
<Route exact path="/MintPage" component={MintPage} />
<Route exact path="/MyCryptonauts" component={MyCryptonauts} />
<Route exact path="/AllCryptonauts" component={AllCryptonauts} />
<Route path="*" component={NotFound} />
</Switch>
</div>
);
}
ReactDOM.render(
<React.StrictMode>
<BrowserRouter><App /></BrowserRouter>
</React.StrictMode>,
document.getElementById("root")
);
我在第一页创建了一个连接按钮,它将我重定向到 mint 部分。
ConnectPage.js
import React from 'react'
export const ConnectPage = (props) => {
async function Connect() {
const web3 = window.web3
const networkId = await web3.eth.net.getId()
if (networkId === 4) {
props.history.push("/MintPage")
} else {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: '0x4' }],
});
props.history.push("/MintPage")
}
}
return (
<div>
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}><img src="https://nftornek.000webhostapp.com/frontend/cnlogo.png"></img></div>
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', marginTop: '5%'}}><button className="connectButton" onClick={Connect}>Enter the Universe</button></div>
</div>
)
}
这是我进行铸币交易的地方。我已经输入 console.log("called checkChainID") 来查看 checkChainID 函数被调用了多少次。每次加载页面时都会调用 12 次,在我尝试导航到同一页面后调用两次。
作为所有这些方面的初学者,我收集了我从教程中获得的信息,这些信息不是很明显,并尝试制作一个带有路由的测试应用程序(我也按照教程做了)
路由的工作原理与我在教程中所做的完全一样,我想继续为我的应用程序使用这个路由示例,但可能由于我缺乏 React 的基础知识,我在页面中做错了.我已经研究这个问题好几个小时了,但我真的不明白我能做些什么来解决它。
我认为是因为 useState,因为它会在我每次调用它时呈现应用程序,但我不确定是否有任何方法可以防止这种情况发生,或者想出更聪明的方法。
MintPage.js
import React, { useState } from 'react';
import Web3 from 'web3'
import Contract from "../src/build/Contract.json";
export const MintPage = (props) => {
const web3 = window.web3;
var [currentAccount, setCurrentAccount] = useState("0x0");
var [currentBalance, setCurrentBalance] = useState("0");
var [mintAmount, setMintAmount] = useState(1);
const [addAmount, setAddAmount] = useState(true);
const [subtractAmount, setSubtractAmount] = useState(false);
window.ethereum.on('chainChanged', (_chainId) => checkChainID());
window.ethereum.on('accountsChanged', (_accounts) => loadBlockchainData());
checkChainID();
async function checkChainID() {
const networkId = await web3.eth.net.getId();
if (networkId !== 4) {
props.history.push("/")
} else {
loadBlockchainData();
}
console.log("called checkChainID")
}
async function loadBlockchainData() {
window.web3 = new Web3(window.ethereum);
const accounts = await web3.eth.getAccounts();
setCurrentAccount(accounts[0]);
getBalance(accounts[0]);
}
async function getBalance(acc) {
const balance = await web3.eth.getBalance(acc);
var balanceEth = web3.utils.fromWei(balance, 'ether');
setCurrentBalance(parseFloat(balanceEth).toFixed(3) + " ETH");
const SmartContractObj = new web3.eth.Contract(Contract.abi, "0x187FF2d65dd7204f11ea0487F2EED36378946902");
}
function MintPage() {
props.history.push("/MintPage")
}
function MyCryptonauts() {
props.history.push("/MyCryptonauts")
}
function AllCryptonauts() {
props.history.push("/AllCryptonauts")
}
function Disconnect() {
props.history.push("/")
}
return (
<div>
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}><img src="https://nftornek.000webhostapp.com/frontend/cnlogo.png" width='500' height='180'></img></div>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<button className="regularButton divide" onClick={MintPage}>Mint</button>
<button className="regularButton divide" onClick={MyCryptonauts}>My Cryptonauts</button>
<button className="regularButton divide" onClick={AllCryptonauts}>All Cryptonauts</button>
<button className="regularButton divide" onClick={Disconnect}>Disconnect</button>
</div>
<div style={{ display: 'flex', justifyContent: 'center' }}><p className="accountText">Current Account: {currentAccount}</p></div>
<div style={{ marginTop: '25%' }}></div>
<div style={{ display: 'flex', justifyContent: 'center' }}><p className="accountText">Mint {mintAmount} Cryptonaut for XXX ETH</p></div>
<div style={{ display: 'flex', justifyContent: 'center' }}><button className="amountButton divide" disabled={subtractAmount ? 0 : 1} onClick={() => {
if (mintAmount <= 1) {
setMintAmount(1);
setSubtractAmount(false);
} else {
setMintAmount(mintAmount - 1);
} if (mintAmount === 2) {
setSubtractAmount(false);
} if (mintAmount >= 1) {
setAddAmount(true);
}
}}>-
</button>
<button className="mintButton divide" onClick={() => {
console.log(mintAmount);
}}>MINT
</button>
<button className="amountButton divide" disabled={addAmount ? 0 : 1} onClick={() => {
if (mintAmount >= 5) {
setMintAmount(5);
} else {
setMintAmount(mintAmount + 1);
}
if (mintAmount === 4) {
setAddAmount(false);
} if (mintAmount >= 1) {
setSubtractAmount(true);
}
}}>+
</button>
</div>
<div style={{ display: 'flex', justifyContent: 'center', marginTop: '7px' }}><p className="accountText">Current Balance: {currentBalance}</p></div>
</div>
)
}
如有任何帮助,我将不胜感激。请指出正确的方向!不过,可能需要更详细的解释,因为我对此太陌生了。 :) 谢谢大家。
有问题的是这三行:
window.ethereum.on('chainChanged', (_chainId) => checkChainID());
window.ethereum.on('accountsChanged', (_accounts) => loadBlockchainData());
checkChainID();
您通常不希望在组件的渲染周期中直接调用函数,因为组件可以出于多种不同的原因重新渲染。每次此组件呈现时,它都会向全局 ethereum
对象添加一个事件侦听器,因此您将在每个组件呈现时获得一个额外的侦听器。由于这是一个功能组件,您应该将这些行包装在一个效果挂钩中,以便您可以控制它们何时 运行.
useEffect(() => {
window.ethereum.on('chainChanged', (_chainId) => checkChainID());
window.ethereum.on('accountsChanged', (_accounts) => loadBlockchainData());
checkChainID();
return () => {
// remove the 'chainChanged' and 'accountsChanged' event listeners
}
}, [])
空数组告诉此效果仅 运行 一次,即组件首次呈现时。如果你需要它 运行 当一些状态改变时,或者你需要访问函数中的任何状态变量 运行 在效果中,你需要列出依赖数组中的那些。
您还需要 return 从钩子中移除监听器的函数,这样他们就不会在每次用户访问页面、离开和 returns.
您可以阅读更多关于 useEffect
here。