【问题标题】:React router component calls function multiple timesReact 路由组件多次调用函数
【发布时间】:2021-11-22 17:41:13
【问题描述】:

我正在尝试学习区块链开发,所以几周前我开始学习 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")
);

我在第一页创建了一个连接按钮,它将我重定向到薄荷部分。

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>

  )
}

我将非常感谢任何帮助。请指出我正确的方向!不过,可能需要更多详细的解释,因为我对此太陌生了。 :) 谢谢大家。

【问题讨论】:

    标签: javascript reactjs react-native react-router web3


    【解决方案1】:

    有问题的部分是这三行:

    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
      }
    }, [])
    

    空数组告诉这个效果只在组件第一次渲染时运行一次。如果您需要它在某些状态发生变化时运行,或者您需要访问效果中运行的函数内的任何状态变量,则需要在依赖数组中列出这些变量。

    您还需要从删除您的侦听器的钩子中返回一个函数,这样他们就不会在每次用户访问页面、离开和返回时继续增加。

    你可以阅读更多关于useEffecthere的信息。

    【讨论】:

    • 哦,这就是我当时正在寻找的,useEffect!非常感谢,它解决了我的问题。我将立即阅读更多相关信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-05
    • 2017-04-09
    • 1970-01-01
    • 2018-05-18
    • 1970-01-01
    • 2019-06-20
    • 2021-12-22
    相关资源
    最近更新 更多