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ā€¦