Errors Unhandled Rejection & Objects are not valid as a React child

I’m having 2 errors I’m stuck in:
1/ I don’t why I get the error and I was having NaN for the Ethereum total.


2/ Was trying to add a component in another one

Here is the code
Assets.jsx

import React, { useEffect, useState } from "react";
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import { makeStyles } from "@material-ui/core/styles";
import { Avatar, Box, Typography } from "@material-ui/core";
import MonetizationOnOutlinedIcon from "@material-ui/icons/MonetizationOnOutlined";
import PieChartIcon from "@material-ui/icons/PieChart";

import { useCoinData } from "../hooks/coinData";
import { c2 } from "../utils";
import Login from "./Login";

import { useMoralis } from "react-moralis";
import Calendar from 'react-calendar';
import 'react-calendar/dist/Calendar.css';

import imageNoteBack from '../data/noteback.jpg';

import Transactions from "./Transactions";


const useStyles = makeStyles((theme) => ({
  tokenImg: {
    height: "2rem",
    width: "2rem",
  },
}));

export default function Assets() {


  const { isAuthenticated } = useMoralis();

  const [valueDate, onChange] = useState(new Date());


      useEffect(() => {
        if(isAuthenticated){
            onChangeDate(valueDate); 
            console.log("valueDate in useeffect: " + valueDate)
    }
  }, [isAuthenticated]); 

    async function onChangeDate(nextValue) {
      onChange(nextValue);
      console.log("nextValue: " + nextValue)
      console.log("valueDate: " + valueDate)
    }


  const { coinList, portfolioValue, isLoading } = useCoinData();
  const styles = useStyles();

  if (!coinList || !coinList.length || isLoading) {
    return (
      <Box display="flex" flexDirection="column" alignItems="center">
        <Typography variant="h4" gutterBottom>DDiary</Typography>
        <Typography>Connect an Ethereum wallet to manage your diary</Typography>
        <Login />
      </Box>
    );
  }

  return (
    <div style={{ backgroundImage: `url(${imageNoteBack})` }}>
      <Box my={2}>
        <Typography variant="h5" my={2}>
          {c2.format(portfolioValue)}
          <PieChartIcon fontSize="small" />
        </Typography>
      </Box>
      <Card variant="outlined">
        <CardContent>
          <Typography gutterBottom>All Assets</Typography>
          {coinList.map((token, i) => (
            <Box display="flex" justifyContent="space-between" mb={2} key={i}>
              <Box display="flex">
                <Box display="flex" alignItems="center">
                  {token.image ? (
                    <Avatar
                      className={styles.tokenImg}
                      src={token.image}
                      alt={token.symbol}
                    />
                  ) : (
                    <Avatar>
                      <MonetizationOnOutlinedIcon fontSize="large" />
                    </Avatar>
                  )}
                </Box>
                <Box display="flex" flexDirection="column" ml={1}>
                  <Typography variant="subtitle2">{token.name}</Typography>
                  <Typography variant="body1">
                    {token.valueTxt} {c2.format(token.price)}
                  </Typography>
                </Box>
              </Box>
              <Typography variant="body1">{c2.format(token.value)}</Typography>
            </Box>
          ))}
        </CardContent>
      </Card>
      <Card variant="outlined">
        <CardContent>
        <div style={{display: 'flex', justifyContent: 'center', alignItems:'center'}}>
      <Calendar
        onChange={onChangeDate}
        value={valueDate}
        maxDate={new Date()}
      />
    </div>
    <br />
    <div id="container">
      <Transactions date={valueDate}/>
      </div>
      </CardContent>
      </Card>
    </div>
  );
}

Transactions.jsx

import React, { useEffect, useState } from "react";
import pageflip from "../data/pageflip.mp3";
import { Moralis } from "moralis";

function saveToMoralis () {
    alert("button was clicked");
  }

function millisecondsToTime (ms) {
    let minutes = Math.floor(ms / (1000 * 60));
    let hours = Math.floor(ms / (1000 * 60 * 60));
    let days = Math.floor(ms / (1000 * 60 * 60 * 24));
    
    if (days < 1) {
        if (hours < 1) {
            if (minutes < 1) {
                return `less than a minute ago`
            } else return `${minutes} minutes(s) ago`
        } else return `${hours} hours(s) ago`
    } else return `${days} days(s) ago`
  }

  const pageflipAudio = new Audio(pageflip);
  const playSound = audioFile => {
    audioFile.play();
}


export default async function setDivDate ({date}) {

    //const { Moralis } = useMoralis();
    
    // set lessThan date
    let nextDate = new Date(date);
    nextDate.setDate(nextDate.getDate()+1);
                
    let query = new Moralis.Query('EthTransactions')
    query.greaterThan("block_timestamp", date);
    query.lessThan("block_timestamp", nextDate);
    const resultDateTransaction = await query.find()


    if (resultDateTransaction.length > 0) {

    return (
      <table border = "1" bordercolor = "blue">
      <caption>${resultDateTransaction.length} transaction(s) ${date.toLocaleDateString()}</caption>
      <thead>
      <tr>
      <th scope="col">Transaction</th>
      <th scope="col">Block Number</th>
      <th scope="col">Age</th>
      <th scope="col">Type</th>
      <th scope="col">Fee</th>
      <th scope="col">Value</th>
      <th scope="col">Notes</th>
          </tr>
      </thead>
      <tbody>
      {resultDateTransaction.forEach((t) => {
    return (
    <tr>
        <td><a href='https://etherscan.io/tx/${t.attributes.hash}' target="_blank" rel="noopener noreferrer">${t.attributes.hash}</a></td>
        <td><a href='https://etherscan.io/block/${t.attributes.block_number}' target="_blank" rel="noopener noreferrer">${t.attributes.block_number}</a></td>
        <td>${millisecondsToTime(Date.parse(new Date()) - Date.parse(t.attributes.block_timestamp))}</td>
        <td>${t.attributes.from_address == Moralis.User.current().get('ethAddress') ? 'Outgoing' : 'Incoming'}</td>
        <td>${((t.attributes.gas * t.attributes.gas_price) / 1e18).toFixed(5)} ETH</td>
        <td>${(t.attributes.value / 1e18).toFixed(5)} ETH</td>
        <td><input type="text" id="name" name="name"></input><button>Save</button></td>
    </tr>
    );
})}
      </tbody>
      </table>
    );

    playSound(pageflipAudio);

}
}

CoinData.js

import { useMemo } from "react";
import { useEffect } from "react";
import { useState } from "react";
import { useMoralis, useMoralisCloudFunction } from "react-moralis";
import coinGeckoList from "../data/coinGeckoTokenList.json";
import { tokenValue, tokenValueTxt } from "../utils";

const emptyList = [];
const coinGeckoApiUrl = "https://api.coingecko.com/api/v3/coins/markets";

export const useCoinData = () => {
  const { user } = useMoralis();
  const userAddress = useMemo(() => user?.attributes.ethAddress, [user]);
  const { data: tokens, isLoading } = useMoralisCloudFunction("getTokens", {
    userAddress,
  });
  const [coinList, setCoinList] = useState(emptyList);
  const [portfolioValue, setPortfolioValue] = useState(0);

  useEffect(() => {
    if (tokens?.length) {
      // get list of CoinGecko IDs for user tokens
      const ids = tokens
        .map((token) => coinGeckoList[token.symbol.toLowerCase()]?.id)
        .filter((id) => Boolean(id))
        .join(",");
      const url = `${coinGeckoApiUrl}?vs_currency=usd&ids=${ids}`;
      console.log("url:", url);

      // fetch coin price data by ID
      fetch(url, {
        method: "GET",
        mode: "cors",
        headers: { "Access-Control-Allow-Origin": true },
      })
        .then((response) => response.json())
        .then((data) => {
          // pivot into a dictionary
          console.log("fetch data:", data);
          const marketData = {};
          data.forEach((d) => (marketData[d.symbol.toUpperCase()] = d));
          console.log("marketData:", marketData);
          return marketData;
        })
        .then((data) => {
          // add token balance, formatted output for UI
          let totalBalance = 0;
          const newList = tokens.map((token) => {
            const output = { ...token };
            const tokenData = data[token.symbol.toUpperCase()];
            output.price = tokenData?.current_price || 0;
            output.image = tokenData?.image;
            output.amount = tokenValue(+output.balance, +output.decimals);
            output.value = output.price ? output.amount * output.price : 0;
            totalBalance += output.value;
            output.valueTxt = tokenValueTxt(
              +output.balance,
              +output.decimals,
              output.symbol
            );
            return output;
          });
          console.log("output list:", newList);
          setCoinList(newList);
          setPortfolioValue(totalBalance);
        });
    } else {
      setCoinList(emptyList);
    }
  }, [tokens]);

  return { coinList, isLoading, portfolioValue };
};
1 Like

Please share your repo
or upload hooks/coinData

here it is:

Hey @nadtn

Check your cloud function, it doesn’t return ETH balance - that’s why it shows NaN.
The repo you shared doesn’t have Transactions.jsx and works without any bugs.

In the repo wasn’t updated (that was the last working version), can you please check now?
Can’t test since I have errors but changed:

  const balQuery = new Moralis.Query("_EthAddress");
  balQuery.equalTo("objectId", userAddress);
  const balResult = await balQuery.first({useMasterKey: true});

  results.push({
    name: "Ethereum",
    symbol: "ETH",
    balance: balResult.get("EthBalance"),
    decimals: 18
  });

To

  const balQuery = new Moralis.Query("EthAddress");
  balQuery.equalTo("address", userAddress);
  const balResult = await balQuery.first({useMasterKey: true});

  results.push({
    name: "Ethereum",
    symbol: "ETH",
    balance: balResult.get("balance"),
    decimals: 18
  });

Saved in the cloud functions section in the server, I don’t know when it’ll be updated in my project or do I have to do it manually?

What I changed in the cloud function created an error. I’ll let you check first with the old one then I’ll look into the new one

The component name should match the name of the exported function:

You need to use:
export default function Transactions(date)
Instead of:
export default async function setDivDate(date)

It’s a bad practise to return async component like you did. All async logic should be outside component fucntion .

I suggest you to use accredited names for useState() hooks:
Correct:
const [name,setName] = useState()
const [date, setDate] = useState();
Not Correct:
const [valueDate, onChange] = useState();

I spent a very long time working on your code, but it has a lot of errors. I suggest you learn React with something simpler. Make a to-do for example.

Just spend a couple of days studying the React itself :man_factory_worker:

Your cloud fucntion should be like:

// this goes in the Moralis server Cloud Functions section
Moralis.Cloud.define("getTokens", async(request) => {
  const {userAddress} = request.params;
  if (!userAddress){
    return [];
  }
  
  const tokenQuery = new Moralis.Query("EthTokenBalance");
  tokenQuery.equalTo("address", userAddress);
  const tokenResult = await tokenQuery.find();
  
  const results = tokenResult.map((token) => token.attributes);
  
  const balQuery = new Moralis.Query("EthBalance");
  balQuery.equalTo("address", userAddress);
  const balResult = await balQuery.first({useMasterKey: true});

  results.push({
    name: "Ethereum",
    symbol: "ETH",
    balance: balResult.get("balance"),
    decimals: 18
  });
  
  return results;
});

You need to query the EthBalance table isntead of _EthAddress

Ok I took into consideration your remarks, this is the new Transaction.jsx

import React, { useEffect, useState } from "react";
import pageflip from "../data/pageflip.mp3";
import { Moralis } from "moralis";

async function getTransactions (date) {

    console.log("date in getTransactions: " + date)
    // set lessThan date
    let nextDate = new Date(date);
    nextDate.setDate(nextDate.getDate()+1);
                
    let query = new Moralis.Query('EthTransactions')
    query.greaterThan("block_timestamp", date);
    query.lessThan("block_timestamp", nextDate);
    const resultDateTransaction = await query.find()

    return resultDateTransaction;
  }

function millisecondsToTime (ms) {
    let minutes = Math.floor(ms / (1000 * 60));
    let hours = Math.floor(ms / (1000 * 60 * 60));
    let days = Math.floor(ms / (1000 * 60 * 60 * 24));
    
    if (days < 1) {
        if (hours < 1) {
            if (minutes < 1) {
                return `less than a minute ago`
            } else return `${minutes} minutes(s) ago`
        } else return `${hours} hours(s) ago`
    } else return `${days} days(s) ago`
  }

  const pageflipAudio = new Audio(pageflip);
  const playSound = audioFile => {
    audioFile.play();
}


export default function Transactions ({date}) {


    const resultDateTransaction = getTransactions (date);

    if (resultDateTransaction.length > 0) {

    return (
      <table border = "1" bordercolor = "blue">
      <caption>${resultDateTransaction.length} transaction(s) ${date.toLocaleDateString()}</caption>
      <thead>
      <tr>
      <th scope="col">Transaction</th>
      <th scope="col">Block Number</th>
      <th scope="col">Age</th>
      <th scope="col">Type</th>
      <th scope="col">Fee</th>
      <th scope="col">Value</th>
      <th scope="col">Notes</th>
          </tr>
      </thead>
      <tbody>
      {resultDateTransaction.forEach((t) => {
    return (
    <tr>
        <td><a href='https://etherscan.io/tx/${t.attributes.hash}' target="_blank" rel="noopener noreferrer">${t.attributes.hash}</a></td>
        <td><a href='https://etherscan.io/block/${t.attributes.block_number}' target="_blank" rel="noopener noreferrer">${t.attributes.block_number}</a></td>
        <td>${millisecondsToTime(Date.parse(new Date()) - Date.parse(t.attributes.block_timestamp))}</td>
        <td>${t.attributes.from_address == Moralis.User.current().get('ethAddress') ? 'Outgoing' : 'Incoming'}</td>
        <td>${((t.attributes.gas * t.attributes.gas_price) / 1e18).toFixed(5)} ETH</td>
        <td>${(t.attributes.value / 1e18).toFixed(5)} ETH</td>
        <td><input type="text" id="name" name="name"></input><button>Save</button></td>
    </tr>
    );
})}
      </tbody>
      </table>
    );

    playSound(pageflipAudio);

}
}

Works fine I guess.
But I still get


I’ve also changed the cloud function like you said (the same as I suggested).
I don’t get why the errors says Transactions and points to coinData… Is a default needed for Transactions?

But yes it’s my first time using React, thought I would learn while building but I’ll definitely go back to the basics after the ETHOnline ends. I’m also thinking of integrating the academy. :wink:

I guess this happens because of this:
if (resultDateTransaction.length > 0) { }
You always need to return something for render from the component.

So make something like:

export default function Transactions({ date }) {
        if (resultDateTransaction.length > 0) {
          return <>Your code</>;
        } else {
          return <>Loading</>;
        }
      }

The default I was mentioning, I get it, it works.
But now that I have my async logic outside the component:

export default function Transactions ({date}) {


    const resultDateTransaction = getTransactions (date);
    console.log("Transactions: " + resultDateTransaction)
    console.log("Transactions lengh: " + resultDateTransaction.length)
    
    if (resultDateTransaction.length > 0) {

I have this result:
Transactions: [object Promise]
Transactions lengh: undefined

But inside getTransactions I have:
Transactions in getTransactions: [object Object],[object Object]

Why am I not having the same value? Promise is for async right?

const resultDateTransaction = await query.find()


    if (resultDateTransaction.length > 0) {

You are trying to get length of the promise

I suggest you to create new hook for the query. As an example you can use useCoinData

So the logic will be quite simple. If it is fetching you return [] when it is fetched you return an array with objects

I didn’t get it, can you elaborate?
Tried like you said

export default function Transactions ({date}) {


    const resultDateTransaction = useTransactionsData (date);
    console.log("Transactions: " + resultDateTransaction)
    console.log("Transactions lengh: " + resultDateTransaction.length)

And this is transactionsData.js


import { Moralis } from "moralis";

export const useTransactionsData = (date) => {

    console.log("date in getTransactions: " + date)
    // set lessThan date
    let nextDate = new Date(date);
    nextDate.setDate(nextDate.getDate()+1);
                
    let query = new Moralis.Query('EthTransactions')
    query.greaterThan("block_timestamp", date);
    query.lessThan("block_timestamp", nextDate);
    const resultDateTransaction = query.find()

    console.log("Transactions in get: " + resultDateTransaction)

    return resultDateTransaction;
  };

With same result:

just noticed this:

=>
const resultDateTransaction = await query.find()

True thanks CK!
Because I had this I removed it:


Tried to add async but didn’t work. Not used to it to tell you the truth.

You can find an example with async await here: React Error: You need to call Parse.initialize before using Parse

Thanks so here we go:

import { Moralis } from "moralis";

export const useTransactionsData = async (date) => {

    // set lessThan date
    let nextDate = new Date(date);
    nextDate.setDate(nextDate.getDate()+1);
                
    let query = new Moralis.Query('EthTransactions')
    query.greaterThan("block_timestamp", date);
    query.lessThan("block_timestamp", nextDate);
    const resultDateTransaction = await query.find()

    console.log("Transactions in get: " + resultDateTransaction)

    return resultDateTransaction;
  };

But I still get [object Promise] in resultDateTransaction

export default function Transactions ({date}) {


    const resultDateTransaction = useTransactionsData (date);
    console.log("Transactions: " + resultDateTransaction)
    console.log("Transactions lengh: " + resultDateTransaction.length)

When I have [object Object],[object Object] with resultDateTransaction
Guess I’m not using the hook the right way…