I am trying to make 7 api calls right away when the component mounts and get data quickly, then keep on making an API call for each of the 3 methods to getTemp, getGpu, getMemory. Although it's taking about 20 seconds to get 1 API call.
I am looking at my backend and everything work fine, the calls are received and responded correctly
I tried to remove asyc calls but that didn't work
Explained in details: Right now:
Refresh page -> backend API gets hit with 3 calls -> nothing is shown in the console or browser
20 seconds later -> all my state arrays are empty
Circa 25 seconds later -> arrays have 1 entry
1 second later -> arrays start to have multiple entries as they should and works fine
What I want to happen:
From Network tabs I keep getting all the API calls pending:
Eventually it clears up but it takes between 2 and 3 mins to start clearing up
Can you help with making the 7 API calls once only and making 1 api call every second?
import { Card, Metric, CategoryBar, Flex, Text, AreaChart, ProgressBar, Title, LineChart } from "@tremor/react";
import card_1 from '../assets/cards/card_1.json'
import card_2 from '../assets/cards/card_2.json'
import card_3 from '../assets/cards/card_3.json'
import card_4 from '../assets/cards/card_4.json'
import React, { useState, useEffect } from 'react';
import axios from 'axios';
var all_cards_array = [card_1, card_2, card_3, card_4]
var array_of_temperatures = []
var array_of_gpu_usage = []
var array_of_gpu_memory = []
const array_of_colors = [
"slate",
"gray",
"zinc",
"neutral",
"stone",
"red",
"orange",
"amber",
"yellow",
"lime",
"green",
"emerald",
"teal",
"cyan",
"sky",
"blue",
"indigo",
"violet",
"purple",
"fuchsia",
"pink",
"rose"
];
var startTime, endTime;
var optionCelsius = {
style: 'unit',
unit: 'celsius'
};
var optionPercent = {
style: 'percent',
minimumFractionDigits: 0,
maximumFractionDigits: 0
};
axios.defaults.baseURL = 'http://url';
const valueFormatterPercentage = (number) => `${new Intl.NumberFormat("en-US", optionPercent).format(number / 100).toString()}`;
const valueFormatterCelcius = (number) => `${new Intl.NumberFormat("en-US", optionCelsius).format(number).toString()}`;
const Dashboard = () => {
const [temperatures, setTemperatures] = useState(null);
const [gpuUsage, setGpuUsage] = useState(null);
const [gpuMemory, setGpuMemory] = useState(null);
const [count, setCount] = useState(10);
const getTemps = async () => {
try {
const { data } = await axios.get('/temperature_info');
await setTemperatures(data);
converTemperatureJsonToArray()
console.log('Temps')
console.log(array_of_temperatures)
console.log(temperatures)
return data
} catch (err) {
console.error(err);
}
};
const getGpuUsage = async () => {
try {
const { data } = await axios.get('/gpu_usage_info');
await setGpuUsage(data);
converGpuJsonToArray()
console.log('Gpu Usage')
console.log(array_of_gpu_usage)
console.log(gpuUsage)
return data
} catch (err) {
console.error(err);
}
};
const getGpuMemory = async () => {
try {
const { data } = await axios.get('/memory_usage_info');
await setGpuMemory(data);
converMemoryJsonToArray()
console.log('Memory')
console.log(array_of_gpu_memory)
console.log(gpuMemory)
return data
} catch (err) {
console.error(err);
}
};
const countDown = () => {
const interval = setInterval(function () {
setCount((prev) => prev - 1);
}, 1000);
};
useEffect(() => {
getTemps()
getGpuUsage()
getGpuMemory()
gpuUsageKeys()
gpuMemoryKeys()
temperaturesKeys()
// countDown()
}, [temperatures, gpuUsage, gpuMemory]);
function start() {
startTime = new Date();
}
function end() {
endTime = new Date();
var timeDiff = endTime - startTime; //in ms
// strip the ms
timeDiff /= 1000;
// get seconds
var seconds = Math.round(timeDiff);
console.log(seconds + " seconds");
}
function select_random_color(array_lenght) {
const list_colors = []
for (i in array_lenght) {
list_colors.push(array_of_colors[Math.floor(Math.random() * array_lenght)])
}
return list_colors
}
function temperaturesKeys() {
if (temperatures) {
console.log('In keys temperatures')
var list_keys = []
for (const key of Object.keys(temperatures)) {
if (key !== 'time_stamp') {
list_keys.push(key)
}
}
return list_keys
}
return null
}
function gpuUsageKeys() {
if (gpuUsage) {
console.log('In keys gpu')
var list_keys = []
for (const key of Object.keys(gpuUsage)) {
if (key !== 'time_stamp') {
list_keys.push(key)
}
}
return list_keys
}
return null
}
function gpuMemoryKeys() {
if (gpuMemory) {
console.log('In keys memory')
var list_keys = []
for (const key of Object.keys(gpuMemory)) {
if (key !== 'time_stamp') {
list_keys.push(key)
}
}
return list_keys
}
return null
}
function getTemperatureFormattedJson() {
const currentDate = new Date().toLocaleTimeString()
const dict = {}
for (const key of Object.keys(temperatures)) {
if (key === 'time_stamp') {
dict[key] = currentDate
} else {
dict[key] = temperatures[key]
}
}
return dict
}
function getGpuFormattedJson() {
const currentDate = new Date().toLocaleTimeString()
const dict = {}
for (const key of Object.keys(gpuUsage)) {
if (key === 'time_stamp') {
dict[key] = currentDate
} else {
dict[key] = gpuUsage[key]
}
}
return dict
}
function getMemoryFormattedJson() {
const currentDate = new Date().toLocaleTimeString()
const dict = {}
for (const key of Object.keys(gpuMemory)) {
if (key === 'time_stamp') {
dict[key] = currentDate
} else {
dict[key] = gpuMemory[key]
}
}
return dict
}
function converTemperatureJsonToArray() {
if (temperatures) {
if (array_of_temperatures.length > 7) {
array_of_temperatures.splice(0, 1)
}
array_of_temperatures.push(getTemperatureFormattedJson())
return array_of_temperatures
}
return [""]
}
function converGpuJsonToArray() {
if (gpuUsage) {
if (array_of_gpu_usage.length > 7) {
array_of_gpu_usage.splice(0, 1)
}
array_of_gpu_usage.push(getGpuFormattedJson())
return array_of_gpu_usage
}
return [""]
}
function converMemoryJsonToArray() {
if (gpuMemory) {
if (array_of_gpu_memory.length > 7) {
array_of_gpu_memory.splice(0, 1)
}
array_of_gpu_memory.push(getMemoryFormattedJson())
return array_of_gpu_memory
}
return [""]
}
function renderBox() {
return (
<div className={color}>
</div>
)
}
function uuidv4() {
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
}
function renderGPUName(cardName) {
return (
<div key={uuidv4()}>
<h2 className='text-2xl font-bold'>
Gpu Name
</h2>
<br />
<Card className="max-w-xs mx-auto" decoration="top" decorationColor="indigo">
<Metric>{cardName}</Metric>
</Card>
</div>
)
}
function renderCards() {
const cards = []
var div_top_black = 'black'
var div_top_blue = 'grid grid-cols-5 gap-14 blue'
const list_colors = [div_top_black, div_top_blue]
var count = 0
for (const card of all_cards_array) {
var color = ''
if (count == 0) {
color = list_colors[0]
count += 1
} else {
color = list_colors[1]
count = 0
}
cards.push(
<div key={uuidv4()}>
<br />
<br />
<br />
<br />
</div>
)
cards.push(
<div className={color} key={uuidv4()}>
</div>
)
cards.push(renderGPUName(card.name))
cards.push(
<div key={uuidv4()}>
<br />
<br />
<br />
<br />
</div>
)
cards.push(
<div className='grid grid-cols-5 gap-14' key={uuidv4()}>
<div>
<h2 className='text-2xl font-bold'>
Driver Version
</h2>
<br />
<Card className="max-w-xs mx-auto" decoration="top" decorationColor="indigo">
<Metric>{card.driver_version}</Metric>
</Card>
</div>
<div>
<h2 className='text-2xl font-bold'>
P State
</h2>
<br />
<Card className="max-w-xs mx-auto" decoration="top" decorationColor="indigo">
<Metric>{card.pstate}</Metric>
</Card>
</div>
<div>
<h2 className='text-lg font-bold'>
Pcie Link Gen Max
</h2>
<br />
<Card className="max-w-xs mx-auto" decoration="top" decorationColor="indigo">
<Metric>{card.pcie_link_gen_max}</Metric>
</Card>
</div>
<div>
<h2 className='text-lg font-bold'>
Pcie Link Gen Current
</h2>
<br />
<Card className="max-w-xs mx-auto" decoration="top" decorationColor="indigo">
<Metric>{card.pcie_link_gen_current}</Metric>
</Card>
</div>
<div>
<h2 className='text-2xl font-bold'>
Temperature
</h2>
<br />
<Card className="max-w-sm mx-auto">
<Flex>
<Text>0 • {card.temperature_gpu}C</Text>
<Text>200</Text>
</Flex>
<ProgressBar value={card.temperature_gpu} color="red" className="mt-3" />
</Card>
</div>
<div>
<h2 className='text-2xl font-bold'>
Utilization Gpu
</h2>
<br />
<Card className="max-w-sm mx-auto">
<Flex>
<Text>0 • {card["utilization_gpu [%]"]} %</Text>
<Text>100</Text>
</Flex>
<ProgressBar value={card["utilization_gpu [%]"]} color="red" className="mt-3" />
</Card>
</div>
<div>
<h2 className='text-xl font-bold'>
Utilization Memory
</h2>
<br />
<Card className="max-w-sm mx-auto">
<Flex>
<Text>0 • {card["utilization_memory [%]"]}%</Text>
<Text>100</Text>
</Flex>
<ProgressBar value={card["utilization_memory [%]"]} color="red" className="mt-3" />
</Card>
</div>
<div>
<h2 className='text-2xl font-bold'>
Memory Total
</h2>
<br />
<Card className="max-w-sm mx-auto">
<Flex>
<Text>0 • {card["memory_total [MiB]"]} MiB</Text>
<Text>{card["memory_total [MiB]"]}</Text>
</Flex>
<ProgressBar value={card["memory_total [MiB]"]} color="red" className="mt-3" />
</Card>
</div>
<div>
<h2 className='text-2xl font-bold'>
Memory Free
</h2>
<br />
<Card className="max-w-sm mx-auto">
<Flex>
<Text>0 • {card["memory_free [MiB]"]} MiB</Text>
<Text>{card["memory_total [MiB]"]}</Text>
</Flex>
<ProgressBar value={card["memory_free [MiB]"] / card["memory_total [MiB]"] * 100} color="red" className="mt-3" />
</Card>
</div>
<div>
<h2 className='text-2xl font-bold'>
Memory Used
</h2>
<br />
<Card className="max-w-sm mx-auto">
<Flex>
<Text>0 • {card["memory_used [MiB]"]} MiB</Text>
<Text>{card["memory_total [MiB]"]}</Text>
</Flex>
<ProgressBar value={card["memory_used [MiB]"] / card["memory_total [MiB]"] * 100} color="green" className="mt-3" />
</Card>
<br />
<br />
</div>
</div>
)
}
return cards
}
return (
<>
<div className='grid grid-cols-1 gap-14'>
{array_of_temperatures.length > 7 ? 'Fetched' : 'Loading... ' + count + ' seconds left'}
<br />
<br />
<br />
<br />
<Card>
<Title>Cards Temperature</Title>
<LineChart
className="mt-6"
data={array_of_temperatures.length > 7 ? array_of_temperatures : null}
index="time_stamp"
categories={temperatures ? temperaturesKeys() : []}
colors={array_of_temperatures > 0 ? select_random_color(temperaturesKeys().length - 1) : ["neutral", "indigo", "orange", "green"]}
valueFormatter={valueFormatterCelcius}
yAxisWidth={100}
/>
</Card>
</div>
<br />
<br />
<div className='grid grid-cols-1 gap-14'>
<Card>
<Title>Cards Gpu Usage</Title>
<LineChart
className="mt-6"
data={array_of_gpu_usage.length > 7 ? array_of_gpu_usage : null}
index="time_stamp"
categories={gpuUsage ? gpuUsageKeys() : []}
colors={array_of_gpu_usage > 0 ? select_random_color(gpuUsageKeys().length - 1) : ["neutral", "indigo", "orange", "green"]}
valueFormatter={valueFormatterPercentage}
yAxisWidth={100}
/>
</Card>
</div>
<br />
<br />
<div className='grid grid-cols-1 gap-14'>
<Card>
<Title>Cards Memory Usage</Title>
<LineChart
className="mt-6"
data={array_of_gpu_memory.length > 7 ? array_of_gpu_memory : null}
index="time_stamp"
categories={gpuMemory ? gpuMemoryKeys() : []}
colors={array_of_gpu_memory > 0 ? select_random_color(gpuMemoryKeys().length - 1) : ["neutral", "indigo", "orange", "green"]}
valueFormatter={valueFormatterPercentage}
yAxisWidth={100}
/>
</Card>
</div>
<br />
<br />
{renderCards()}
</>
)
};
export default Dashboard;
I think I had to step back and see a different approach to this. I am adding below some references to justify my response.
First, we need to break the "If the only tool you have is a hammer, you tend to see every problem as a nail"
So when to use api calls and webhooks?
This is a really good article that set me on the right path: Webhooks, Api Calls, Push Sub, and Websockets differences
Second, let's implement the websocket:
const WS_URL_ALL_INFO = 'ws://YOUR_WEBSOCKET_URL';
const socketState = 'OPEN'
const { sendJsonMessage, lastJsonMessage } = useWebSocket(WS_URL_ALL_INFO, {
share: true,
})
useEffect(() => {
sendJsonMessage({ "message": socketState })
if (lastJsonMessage) {
console.log(lastJsonMessage)
}
}, [jsonMessage, lastJsonMessage])
That's it! all it needed it's a websocket, you can use a setTimeout to set a delay if you want and you can use multiple useEffect() with [] to run once multiple times if you want
To @Leroy you are using polling, which in this case is the wrong approach, I know I was thinking the same way, then it hit me. Polling will always be slow, webhooks will not work either as it will be slow too since there is a call every 1 second. A websocket is opened and stays open until you close it, continuously gives you data and you can either establish a timeout server or client side. In this case it works perfectly.
Also:
ReactDOM.createRoot(
document.getElementById("root")
).render( <div>
<Widget name="Temperature" delay="200" />
<Widget name="GpuUsage" delay="400" />
</div>
);
In react 18 using the dom directly is deprecated
ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it’s running React 17. Learn more: https://reactjs.org/link/switch-to-createroot