-1

I have a basic template of a react website with a button that connects to your metamask wallet and displays the current balance.

I have the classic issue that the state of 'addressBalance' only becomes visible after the second click on the connect wallet button. (Basically: I need to click 2 times to show the changes of the 'addressBalance' state.)

I added a useEffect hook that gets triggered whenever 'addressBalance' state changes however the useEffect also only gets triggered on loading the page & after the second click.

This is my code:

import "./App.css";
import { useState, useEffect } from "react";
import { ethers } from "ethers";

function App() {
  const [walletAddress, setWalletAddress] = useState("");
  const [addressBalance, setaddressBalance] = useState("");

  useEffect(() => {
    console.log("Balance updated:", addressBalance);
  }, [addressBalance]);

  async function requestAccount() {
    console.log("Requesting account...");

    if (window.ethereum) {
      console.log("detected");

      try {
        const accounts = await window.ethereum.request({
          method: "eth_requestAccounts",
        });

        setWalletAddress(accounts[0]);
        getBalance();
      } catch (error) {
        console.log("Error connecting...");
      }
    } else {
      alert("Meta Mask not detected");
    }
  }

  function getBalance() {
    const result = window.ethereum
      .request({
        method: "eth_getBalance",
        params: [walletAddress, "latest"],
      })
      .then((result) => {
        console.log(result);
        let wei = parseInt(result, 16);
        let balance = wei / 10 ** 18;
        setaddressBalance(balance);
      });
  }

  async function connectWallet() {
    if (typeof window.ethereum !== "undefined") {
      await requestAccount();

      const provider = new ethers.providers.Web3Provider(window.ethereum);
    }
  }

  return (
    <div className="App">
      <header className="App-header">
        <button className="request-account" onClick={requestAccount}>
          Request Account
        </button>
        <h3>Wallet Address: {walletAddress}</h3>
        <h3>
          {!walletAddress
            ? "No address connected..."
            : `Balance: ${addressBalance} ETH`}
        </h3>
      </header>
    </div>
  );
}

export default App;

I'd like to know how it is possible that the useEffect hook isn't triggered after the first click and how I can fix this problem.

sneaker
  • 49
  • 2
  • 8
  • 1
    Does this answer your question? [The useState set method is not reflecting a change immediately](https://stackoverflow.com/questions/54069253/the-usestate-set-method-is-not-reflecting-a-change-immediately) – Konrad Jan 08 '23 at 14:46
  • 1
    `walletAddress` in `getBalance` has an old value, because `setWalletAddress(accounts[0]);` doesn't change the value in the same render cycle. The new value will be available when the component rerenders. Easy fix: Add a param to the `getBalance(address)` and call it with `getBalance(accounts[0])` – Konrad Jan 08 '23 at 14:47
  • @Konrad Thanks for your answer! How can I make sure that the component re-renders before calling getBalance? If I understand you correctly that would fix the issue. – sneaker Jan 08 '23 at 14:52

1 Answers1

1

Your useEffect hook isn't getting triggered on the first click because when you call setWalletAddress, your getBalance function immediately tries to access the value of walletAddress but because of React's "asynchronous" state update behavior, the walletAddress variable hasn't been updated to accounts[0] by the time you've called getBalance().

An easy way to fix this is to pass the wallet address as an explicit parameter to getBalance:

 async function requestAccount() {
    console.log("Requesting account...");

    if (window.ethereum) {
      console.log("detected");

      try {
        const accounts = await window.ethereum.request({
          method: "eth_requestAccounts",
        });

        setWalletAddress(accounts[0]);
        getBalance(accounts[0]);
      } catch (error) {
        console.log("Error connecting...");
      }
    } else {
      alert("Meta Mask not detected");
    }
  }

  function getBalance(walletAddress) {
    const result = window.ethereum
      .request({
        method: "eth_getBalance",
        params: [walletAddress, "latest"],
      })
      .then((result) => {
        console.log(result);
        let wei = parseInt(result, 16);
        let balance = wei / 10 ** 18;
        setaddressBalance(balance);
      });
  }

  • +1 , this works! So instead of getting the walletAdress from state you pass it on to the getBalance function. How couldn't I think of this ! – sneaker Jan 08 '23 at 14:57