In React, how to render variable from a functional component?

Viewed 512

I am trying to make a counter that counts how many NFTs are minted. I have an async function, getTotalTokensMinted, that gets the total number of tokens minted from a smart contract. How do I render the HTML so it displays the return value, numTokens, from getTotalTokensMinted? Should I use useState hook or local storage?

The code I have below returns a

Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render
import React, { useEffect, useState } from 'react';
import { ethers } from 'ethers';
import './styles/App.css';
import myEpicNFT from './utils/MyEpicNFT.json';

const TOTAL_MINT_COUNT = 3;
const CONTRACTADDRESS = "0x6fe91f4814f372Eb40B547114CD75B76DF5f53dC";

const App = () => {

//const [NFTsMinted, NFTcounter] = useState(0);

 const getTotalTokensMinted = async () => {
   const { ethereum } = window;
   let numTokens;
   try {

     if (ethereum) {
       const provider = new ethers.providers.Web3Provider(ethereum);
       const signer = provider.getSigner();
       const connectedContract = new ethers.Contract(contractAddress, contractABI, signer);

       numTokens = await connectedContract.getTokenNumber();
       return numTokens;
      } 
   } catch (error){
       console.log(error);
   }
   return(
     <div> {numTokens}</div>
   );
 }

return (
   <div className="App">
     <div className="container">
         <p className = "sub-text">
         NFT MINTED = {getTotalTokensMinted} / { TOTAL_MINT_COUNT }
         </p>
       </div>
     </div>
   </div>
 );


}
4 Answers

Your code could quickly get confusing as you develop more functionality. As a rule of thumb, it's good to separate the business logic of your app from the UI.

Try putting your getTotalTokensMinted function in an /api folder or /utils. That function doesn't need to be a component; it simply needs to return a value...

const getTotalTokensMinted = async () => {
   const { ethereum } = window;
   let numTokens;
   try {

     if (ethereum) {
       const provider = new ethers.providers.Web3Provider(ethereum);
       const signer = provider.getSigner();
       const connectedContract = new ethers.Contract(contractAddress, contractABI, signer);

       numTokens = await connectedContract.getTokenNumber();
       return numTokens;
      } 
        return null

   } catch (error){
       console.log(error);
   }

And then in the component you actually want to display the minted count you can simply declare a variable count (or whatever) which will store the number of tokens.

const App = () => {
const count = await getTotalTokensMinted();
return (
   <div className="App">
     <div className="container">
         <p className = "sub-text">
         NFT MINTED = {count} / { TOTAL_MINT_COUNT }
         </p>
       </div>
     </div>
   </div>
 );

}

You may want to get the function returned value like this

return (
   <div className="App">
     <div className="container">
         <p className = "sub-text">
         NFT MINTED = {getTotalTokensMinted()} / { TOTAL_MINT_COUNT }
         </p>
       </div>
     </div>
   </div>
 );

If you get an array object error you need to change on how you return the value in the function like this :

return (
    <div> {numTokens?.map((each)=>{
           <div>{each}</div>
           })}
   </div> 
)

From react point of view you want some async operation and its output in DOM. According to provided code snippet you are calling getTotalTokensMinted in the DOM with incorrect syntax. As a solution you need to call async function in useEffect hook with no dependency to render as componentDidMount. You need local state to store result and then use that state in the DOM.

Steps

Follow these steps as a solution

Define local state

By using useState you can create local state. You can use global state depends on the requirement

const [NFTsMinted, setNFTsMinted] = useState(0);

Async function

Set state in async function as follow

const getTotalTokensMinted = async () => {
    const { ethereum } = window;
    
    if (ethereum) {
      const provider = new ethers.providers.Web3Provider(ethereum);
      const signer = provider.getSigner();
      const connectedContract = new ethers.Contract(contractAddress, contractABI, signer);

      const numTokens = await connectedContract.getTokenNumber();
      setNFTsMinted(numTokens)
    } 
  }

Call async function

Use useEffect hook to call function

  useEffect(() => {
    getTotalTokensMinted()
  }, [])

Display results in DOM

Use state variable to display counts

<div className="App">
  <div className="container">
    <p className = "sub-text">
      NFT MINTED = {NFTsMinted} / { TOTAL_MINT_COUNT }
    </p>
  </div>
</div>
    

You need to wrap your component in the proper syntax:

example: <Route exact path="/" element={Home}

Will give you this same error...Need to correct to this:

<Route exact path="/" element={}

Related