0

I have a simple component that displays a table using react-table. With the mock data set as default with useState it renders fine, and on calling the api using variable data from the text boxes I get status 200 and the proper data returned. The issue I am having is my useTable section of code is running as soon as my useState which contains the api returned DATA runs. useState is async, so at that point is returning undefined as a not yet resolved promise. I read the solution to doing something only after useState is fully resolved would be to use useEffect, however since useTable is a hook I cannot have a hook within a hook, what the heck do I do!

Code:

import { CallApi } from "./Component/callApi";
import React from 'react'

function App() {
  return (
    <div>
      <CallApi />
    </div>
  );
}

export default App;

CallApi.js

import React, { useState, useEffect, useMemo } from "react";
import { COLUMNS } from "./columns";
import { useAsyncDebounce, useTable } from "react-table";
import axios from 'axios'



export const CallApi = () => {

const [buildingName, setBuildingName] = useState('');
const [buildingLevel, setBuildingLevel] = useState('');
const [productionModifierPercentage, setProductionModifier] = useState('');
const [adminCostPercentage, setAdminCostPercentage] = useState('');
const [phase, setPhase] = useState('');
const [result, setResult] = useState(null);
const [DATA, setDATA] = useState([{
  "1": {
    "itemName": "Energy research",
    "profitPerHour": 821.6746356364301
  }
}]);

const [shown, setShown] = useState(true);

const columns = useMemo(() => COLUMNS, []);
const data = useMemo(() => { // Because our nested dictionary returned from fastAPI is an array of one element object, which contains more objects we need to transform this into an array using .map
  const rawData = DATA[0];
  if (rawData != undefined && rawData != null){
    console.log('rawData is no longer undefined or null, now is: '+String(rawData))
  return Object.keys(rawData).map((key) => rawData[key]);
  }
}, [DATA]);

useEffect (() => {
  console.log('data with useEffect: '+JSON.stringify(data))
}, [data]);

useEffect (() => {
  console.log('DATA with useEffect: '+JSON.stringify(DATA))
}, [DATA]);

useEffect (() => {
  ToggleShown();
},[])

const tableInstance = useTable({
    columns,
    data
});

const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow
  } = tableInstance;

const populateTable = async () => {
    try{
        let encodedName = encodeURI(buildingName)
        let targetURI = `http://localhost:8000/api/calculateProfitPerHourOf{}?buildingName=${encodedName}&buildingLevel=${buildingLevel}&productionModifierPercentage=${productionModifierPercentage}&administrationCostPercentage=${adminCostPercentage}`
        let res = await axios.get(targetURI);
        let arr = res.data;
        console.log('arr = '+arr)
        console.log('arr stringified = '+JSON.stringify(arr))
        setDATA(arr)
        return (arr)
        
    } catch(e) {
        console.log(e)
    }
}



const ToggleShown = () => {
  setShown(!shown)
}

const HandleClick = () => {
  populateTable().then (() => {
    console.log('this happened after pouplateTable completed the promise')
  })
}
const whatIsSetShown = async () => {
    ToggleShown();
    console.log('Stringified data: '+String(JSON.stringify(data)))
    console.log('data: '+String(data))
    console.log('DATA = '+DATA)
    console.log('DATA stringified = '+JSON.stringify(DATA))
}



return(
    <div>
        <input type="text" name="buildingName" id="buildingName" placeholder="Building Name" onChange={e => setBuildingName(e.target.value)}></input>
        <input type="text" name="buildingLevel" id="buildingLevel" placeholder="Building Level" onChange={e => setBuildingLevel(e.target.value)}></input>
        <input type="text" name="productionModifier" id="productionModifier" placeholder="Production Modifier %" onChange={e => setProductionModifier(e.target.value)}></input>
        <input type="text" name="adminCost" id="adminCost" placeholder="Administration Cost %" onChange={e => setAdminCostPercentage(e.target.value)}></input>
        <input type="text" name="phase" id="phase" placeholder="Phase"></input>
        <p id='Button'>
        <button type='button' id="getDBinfo" className='block' onClick={()=>HandleClick()}>Get DB Info</button>
        <button type='button' id="getDBinfo" className='block' onClick={()=>whatIsSetShown()}>Print State of setShown</button>
        
        </p> 
        {(shown && DATA) ?
          <table {...getTableProps()}>
            <thead>
              {headerGroups.map((headerGroup) => (
                <tr {...headerGroup.getHeaderGroupProps()}>
                  {headerGroup.headers.map((column) => (
                    <th {...column.getHeaderProps()}>{column.render("Header")}</th>
                  ))}
                </tr>
              ))}
            </thead>
            <tbody {...getTableBodyProps()}>
              {rows.map((row) => {
                prepareRow(row);
                return (
                  <tr {...row.getRowProps()}>
                    {row.cells.map((cell) => {
                      return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>;
                    })}
                  </tr>
                );
              })}
            </tbody>
          </table>
          :null}
    </div> 

) 

};

Notice this section here is the trouble right now:

const tableInstance = useTable({
    columns,
    data
});

How can I only have this run once my useState DATA is resolved, as the const data is set from this using useMemo taking DATA as a dependency which seems to work only running once DATA resolves to a new value and then useMemo updates. But I cannot memoize nor useEffect the tableInstance constance as it's a hook itself!

Error I get upon running:

Uncaught TypeError: Cannot read properties of undefined (reading 'forEach')
    at accessRowsForColumn (useTable.js:591:1)
    at useTable.js:195:1
    at updateMemo (react-dom.development.js:17246:1)
    at Object.useMemo (react-dom.development.js:17886:1)
    at Object.useMemo (react.development.js:1650:1)
    at useTable (useTable.js:57:1)
    at CallApi (callApi.js:46:1)
    at renderWithHooks (react-dom.development.js:16305:1)
    at updateFunctionComponent (react-dom.development.js:19588:1)
    at beginWork (react-dom.development.js:21601:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1)
    at invokeGuardedCallback (react-dom.development.js:4277:1)
    at beginWork$1 (react-dom.development.js:27451:1)
    at performUnitOfWork (react-dom.development.js:26557:1)
    at workLoopSync (react-dom.development.js:26466:1)
    at renderRootSync (react-dom.development.js:26434:1)
    at performConcurrentWorkOnRoot (react-dom.development.js:25738:1)
    at workLoop (scheduler.development.js:266:1)
    at flushWork (scheduler.development.js:239:1)
    at performWorkUntilDeadline (scheduler.development.js:533:1)
    at run (setImmediate.js:40:1)
    at runIfPresent (setImmediate.js:69:1)
    at onGlobalMessage (setImmediate.js:109:1)
1
  • I resolved the issue, I was using DATA[0] in my useMemo mapping from object to array, but this was due to me thinking in error that the response from the api was an array with the data as a single object at the 0th index, in fact this is just how fastAPI returned the results I believe or I don't even know where that went wrong but holy moly I am so happy to have this sucker working. It's always something like that isn't it, trivial and the most time consuming lol. Commented Sep 13, 2022 at 2:23

1 Answer 1

0

I was using DATA[0] in my useMemo mapping from object to array, but this was due to me thinking in error that the response from the api was an array with the data as a single object at the 0th index, at some point this must have been the case but it clearly is no longer. Sheeeeesh.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.