Can't use the data from API when app just starts - reactjs

My data is undefined when the app is started but after the refresh, the data comes perfectly.
For startup
It gives me [Unhandled promise rejection: TypeError: Object.entries requires that input parameter not be null or undefined]
But after the refresh, the data comes perfectly and everything working.
This is part of my data
Object {
"attributes": Object {
"htmlName": null,
"id": 0,
"items": Array [
Object {
"htmlName": "r_1",
"name": "m2 (Brüt)",
"numeric": true,
"options": Object {},
"order": 0,
"required": true,
},
Object {
"htmlName": "r_2",
"name": "m2 (Net)",
"numeric": true,
"options": Object {},
"order": 0,
"required": true,
},
Object {
"htmlName": "r_164",
"name": "Arsa Alanı (m2)",
"numeric": true,
"options": Object {},
"order": 0,
"required": true,
},
Object {
"htmlName": "a_137",
"name": "Oda Sayısı",
"numeric": false,
"options": Object {
"12": "1+0",
"13": "1+1",
"14": "1.5+1",
"15": "2+0",
"16": "2+1",
"17": "2.5+1",
"18": "2+2",
"19": "3+1",
"20": "3.5+1",
"21": "3+2",
"22": "4+1",
"226": "0+1",
"23": "4.5+1",
"24": "4+2",
"25": "4+3",
"26": "4+4",
"27": "5+1",
"28": "5+2",
"29": "5+3",
"30": "5+4",
"31": "6+1",
"32": "6+2",
"33": "6+3",
"34": "7+1",
"35": "7+2",
"36": "7+3",
"37": "8+1",
"38": "8+2",
"39": "8+3",
"40": "8+4",
"41": "9+1",
"42": "9+2",
"43": "9+3",
"44": "9+4",
"45": "9+5",
"46": "9+6",
"47": "10+1",
"48": "10+2",
"49": "10 Üzeri",
},
"order": 0,
"required": true,
},
api.js
export const getData = function () {
return axios
.get(
"blabla",
{
headers: {
Authorization: `blabla`,
},
}
)
.then((json) => {
if (json && json.status === 200) {
//console.log(json);
return json.data;
}
})
.catch((e) => {
console.log(e);
});
};
App.js
const [data, setData] = useState({});
const [roomValue, setRoomValue] = useState(null);
const [roomCount, setRoomCount] = useState([]);
const [isFocus, setIsFocus] = useState(false);
useEffect(() => {
getDataFunc();
//setDropdown(data.attributes.items[3].options);
}, []);
const getDataFunc = async () => {
const res = await getData();
//console.log(res);
setData(res);
console.log(data);
};
function setDropdown(query) {
const response = query;
try {
const entries = Object.entries(response);
const tempArray = [];
for (let i = 0; i < entries.length; i++) {
var key;
var value;
(key = entries[i][0]), (value = entries[i][1]);
tempArray.push({ key: value, value: key });
}
setRoomCount(tempArray);
//console.log(roomCount);
} catch (error) {
//console.log(error);
}
}
How can I fix that ?

Add a seperate useEffect to check wheather the data has been set and then only set the dropdown values
useEffect(() => {
getDataFunc();
}, []);
useEffect(() => {
if(data && data.attributes?.items[3]){
setDropdown(data.attributes.items[3].options);
}
}, [data]);
const getDataFunc = async () => {
const res = await getData();
//console.log(res);
setData(res);
console.log(data);
};

It seems like the error is caused by the attributes property being empty when you try to access it. But when you assign them one by one then it loads because the data is loaded per nested property before assigning it to the variable. Means it hasn't fully loaded yet
const response = data.attributes.items[3].options;
It outputs an error because attributes is undefined. So it's not an object, therefore, attributes.items is considered invalid
// sample
const data = {
/* attributes: {
items: {
1: {
options: 'option1'
},
2: {
options: 'option2'
},
3: {
options: 'option3'
}
}
} */
}
const specificData = data.attributes.items[3].options
console.log(specificData) //
So one solution would be using the optional chaining operator to avoid the error, it's just basically a question mark (?) after the object you are trying to access.
The response would be then 'undefined'. That way even if the attributes is empty or not, data will be assigned to the response constant then you can just add some more checking outside of that.
// sample
const data = {
/* attributes: {
items: {
1: {
options: 'option1'
},
2: {
options: 'option2'
},
3: {
options: 'option3'
}
}
} */
}
const specificData = data.attributes?.items[3].options
console.log(specificData) // outputs undefined instead of an error
Let me know if this works btw. maybe you could provide the actual api or maybe a sample api endpoint so we could test it directly. Or maybe the full code?
I've encoutered this before though I'm not 100% sure this is all I've done. But for the error I'm sure the optional chaining operator will prevent it

Try calling getData inside an async function and wait for the process to complete like this in your App.js
const [data, setData] = useState([]);
const [roomCount, setRoomCount] = useState([]);
useEffect(() => {
getDataFunc()
}, []);
const getDataFunc = async() => {
await getData(setData);
const response = data;
console.log(response);
const entries = Object.entries(response);
const tempArray = [];
for (let i = 0; i < entries.length; i++) {
var key;
var value;
(key = entries[i][0]), (value = entries[i][1]);
tempArray.push({ key: value, value: key });
}
setRoomCount(tempArray);
console.log(roomCount);
}
note: The best practice is not to directly pass the setData function to getData api call instead return the response from api and assign the response in main code like below
const response = await getData();
setData(response)

From what I see, your data.attributes has undefined value.
Please double-check everything, it is technically impossible to get data directly if data.attributes is undefined

Related

array of objects map is not a function

I'm new to React, I'm pulling an API, which is filled with an array of objects.
When trying to map it I get following error: "Uncaught TypeError: benchmarkData.map is not a function"
While I get the error, it actually does the job (when I log it I get the asked result), it just prevents loading the other elements.
This is how my API looks:
[
{
"type": "drgmbmk",
"test": "5782",
"DEAANTA": "412",
"DEGEMAT": "235",
"DEGEMGW": "97.7",
"DEGEMMB": "3.39",
"DEGEMNV": "64.1",
"DEGEMSD": "13.2",
"DEGEMVD": "71.3",
"DEJAART": "2022",
"DELTMCE": "-",
"DEWEENR": "1"
},
{
"type": "drgmbmk",
"test": "5782",
"DEAANTA": "436",
"DEGEMAT": "233",
"DEGEMGW": "120.1",
"DEGEMMB": "3.38",
"DEGEMNV": "64.2",
"DEGEMSD": "13.2",
"DEGEMVD": "71.4",
"DEJAART": "2022",
"DELTMCE": "ALL",
"DEWEENR": "1"
},
{
"type": "drgmbmk",
"test": "5782",
"DEAANTA": "22",
"DEGEMAT": "213",
"DEGEMGW": "99.9",
"DEGEMMB": "3.23",
"DEGEMNV": "64.9",
"DEGEMSD": "12.7",
"DEGEMVD": "72.8",
"DEJAART": "2022",
"DELTMCE": "BBC",
"DEWEENR": "1"
}
]
This is my React code:
useEffect(() => {
if (benchmarkData) {
setSupplierHasData(true);
let loadingplaceFilter = [];
const loadingPlaces = [];
benchmarkData.map(item => { //!start of requested question code
if(item.type === "drlvbmk") {
loadingPlaces.push(item.DELPLCE, item.DELTMCE, item.DEELTCE)
}
}
); //!end of requested question code
console.log(loadingPlaces);
const uniqueLoadingplaces = [];
loadingPlaces.map((item) => {
var findItem = uniqueLoadingplaces.find((x) => x === item);
if (!findItem) uniqueLoadingplaces.push(item);
});
console.log('uniqueloadingplaces', uniqueLoadingplaces);
setFilters({
...filters,
loadingplaces: loadingplaceFilter,
});
setFilterData(true);
}, [benchmarkData]);
Pulling the api:
useEffect(() => {
const fetchBenchmarkData = async () => {
setFetchNewData(false);
console.log('current user active id :', suppliersState.suppliers.activeID, "current user active uuid: ", suppliersState.suppliers.activeUUID);
try {
await axios
.get(
`https://bpg-ap.ddev.site/jsonapi/benchmarking_get_data?supplierid=${suppliersState.suppliers.activeID}&date_from=1&date_to=2022`
)
.then(res => {
setBenchmarkData(res.data);
console.log('BENCHMARKINGRESULTAAT : ', res.data);
});
} catch(error) {
setSupplierHasData(false);
setChartData({});
console.log(error);
}
};
if (benchmarkData === null) {
console.log('useEffect fired, fetch benchmarkData', benchmarkData);
fetchBenchmarkData();
}
}, [benchmarkData, suppliersState.suppliers.activeID]);
Logging benchmarkData results in:
I hope anyone can help me to solve this problem

React does not update nested state using my setter

I'd like to update a react state (the "app" variable) object dynamically. The state has several properties, one of them is a nested object ("app.error" object). There is a kind of setter function ("setAppData") which takes any number of [existing key]: new value pairs of the state as parameter, create a new object and it should update the state according to "temp" object.
It works as intended when I want to update "app.error" using hard-coded variable or any other "app" properties, when I try to update the "app.error" using my setter function, it does not update.
const initialAppData: IAppData = {
error: {} as IFrontEndError,
interfaceTheme: UI_THEME.LIGHT,
};
function AppProvider(props: any) {
const [app, setApp] = useState<IAppData>(initialAppData);
useMemo(() => {
window.addEventListener("error", (event) => {
errorHandler({
colno: event.colno,
filename: event.filename,
lineno: event.lineno,
message: event.message,
});
});
}, []);
const errorHandler = (event: IFrontEndError) => setAppData({error: { ...event }});
const setAppData = (newValue: TUpdateAppData) => {
let key: keyof IAppData;
let temp = getAppData();
for (key in newValue) {
if (
!Object.prototype.hasOwnProperty.call(app, key) ||
!Object.prototype.hasOwnProperty.call(newValue, key)
)
continue;
temp = {
...temp,
[key]: newValue[key],
};
}
setApp({ ...temp, error: { ...temp.error } });
};
const getAppData = () => ({ ...app });
}
Using static update (it works as intended):
const setAppData = () => {
let temp = getAppData();
temp.error = {
colno: 1,
lineno: 1,
message: "asd",
filename: "asd"
}
setApp({ ...temp, error: { ...temp.error } });
};
The structure of the "temp" objects before passing the spreaded copy to the state setter are exactly the same. "temp" object before spreading, triggered by event:
{
"error": {
"colno": 11,
"filename": "http://127.0.0.1:5173/src/components/Debug.tsx?t=1667134926865",
"lineno": 29,
"message": "Error: asd"
},
"interfaceTheme": "light-theme"
}
getAppData() after error event:
{
"error": {},
"interfaceTheme": "light-theme"
}
"temp" object before spreading using hard-coded value:
{
"error": {
"colno": 1,
"lineno": 1,
"message": "asd",
"filename": "asd"
},
"interfaceTheme": "light-theme"
}
getAppData() after execution using hard-coded value:
{
"error": {
"colno": 1,
"lineno": 1,
"message": "asd",
"filename": "asd"
},
"interfaceTheme": "light-theme"
}
What is that I don't notice?
edit: sources of the project:
https://github.com/gazsop/magus_shared
https://github.com/gazsop/magus_react

How to destructure object in React with special characters in key

Backend returns an object, and one of the key-value, I would handle differently, how can I split up object?
useEffect(() => {
const tempUserShortId = getTempUserShortId()
axios({
method: 'get',
url: `questionList?tempUserId=${tempUserShortId}&questionIds[]=6f50dbea-196b-4fb6-82ee-fbade62aab98&questionIds[]=6f50dbea-196b-4fb6-82ee-fbade62aab99&questionIds[]=6f50dbea-196b-4fb6-82ee-fbade62aab9a`,
headers: { crossDomain: true },
}).then((res) => {
const {'6f50dbea-196b-4fb6-82ee-fbade62aab9a', ...rest} = res.data;
setQuestions(rest)
})
}, [ind])
It says: ':' expected.ts(1005
Found this online:
const removeKey = () => {
setEmployee(current => {
// 👇️ remove salary key from object
const {salary, ...rest} = current;
return rest;
});
};
https://bobbyhadz.com/blog/react-remove-key-from-state-object
It does not work.
Got back from backend a structure like this:
{
"6F50DBEA-196B-4FB6-82EE-FBADE62AAB99": {
"text": "Mennyire forradalmi az ETH 2.0 bevezetése?",
"options": {
"1CD0D2C0-7494-4BE6-ADE6-E454816B584E": {
"text": "Forradalmi mert PoW helyett PoS lesz. Kevesebb energiát fog fogyasztani a rendszer. Viszont ez még mindig nem skálázható megoldás, a shardingot még nem tudja, és még hosszú évekig nem is fogja tudni, így a tranzakciós díj ugyanúgy magas fog maradni.",
"rating": {}
}
}
},
"6F50DBEA-196B-4FB6-82EE-FBADE62AAB98": {
You have to focus the key of your object and set it to "_" to tell that you will not use it
const obj = {
"6f50dbea-196b-4fb6-82ee-fbade62aab9a": "cool",
name: "test",
value: 3
}
const {"6f50dbea-196b-4fb6-82ee-fbade62aab9a": _, ...rest} = obj // rest equal {name: "test", value: 3}

Connecting 2 different arrays from the same external link - React hooks fetch

I am able to fetch data what url, but the thing is the url is divided into couple of arrays and I need to fetch data and connect them.
Example:
{
"array1": [
{ "data1": {"name": "Name", "phone": "Phone"}}
]
"array2" : [
{ "data2": { "color": "Color", "car": "Car" } }
]
}
Data hooks :
const userInfo = "URL";
const [userData, setUserData] = useState([]);
useEffect(() => {
getUserInfo();
}, []);
const getUserInfo = async () => {
const response = await fetch(UserInfo);
const jsonData = await response.json();
setUserData(jsonData);
};
Fetch data:
{ userData.data && userData.array1.map((array1, index) =>
<li key={"index" + index}
<h5>{array1.data1.name} </h5>
</li>
)}
I need to connect name from array1 with color from array2, but I can not find the way to do it.
Expected Output : list of data
If you can get those two arrays then you can use this to combine them:
const getUserInfo = async () => {
const response = await fetch(UserInfo);
const jsonData = await response.json();
// this assumes `jsonData` in an object with keys `array1` and `array2`
// if this is not the case, change `jsonData` below to the location of
// those two arrays
const { array1, array2 } = jsonData;
const combinedArray = array1.map(({ data1 }, i) => ({
...data1,
...array2[i].data2 // assumes there is always a corresponding index in array 2
}));
// combinedArray will look like [ { name: 'Name', phone: 'Phone', color: 'Color', car: 'Car' } ] 
setUserData(combinedArray);
};
] 

Using JSON returned from an API to populate a variable used by a React-Table table component

I'm trying to write this React component that connects to an API that returns JSON.
The API returns JSON results that look like this small example:
JSON:
{
"id": 22,
"gameTitle": "The Haunted Plains II",
"startDate": "2020-03-31T12:49:50.009",
"endDate": "2020-04-04T04:00:00",
"description": "A simple bitmap style RPG",
"gameUrl": "https://yourgames.org/1/55/3",
"isOpen": true,
"gameFaqId": 25,
"gameTypeId": 3,
"gameCharClasses": ["Bard", "Theif", "Warrior", "Wizard", "Cleric"]
},
{
"id": 25,
"gameTitle": "Downhill Mountain Biking Pro",
"startDate": "2020-02-12T11:22:30.011",
"endDate": "2020-05-02T03:11:00",
"description": "A side-scrolling, downhill mountaining biking game",
"gameUrl": "https://yourgames.org/3/43/6",
"isOpen": true,
"gameFaqId": 11,
"gameTypeId": 6,
"gameCharClasses": ["Beginner", "Pro", "Super Star"]
}
I want to put the data into the 'gameListing' variable that have defined as 'const gameListing'.
Then I have a function called makeData that is called by the main app which is using a React library called 'react-table'.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
const result = await axios(
'https://localhost:44376/api/projects',
);
setData(result.data);
};
fetchData();
}, []);
const range = len => {
const arr = [];
for (let i = 0; i < len; i++) {
arr.push(i);
}
return arr;
};
const gameListing = data.map(g => {
return {
id: g.id,
gameTitle: g.gameTitle,
startDate: g.startDate,
endDate: g.endDate,
description: g.description,
gameUrl: g.gameUrl,
isOpen: g.isOpen,
gameFaqId: g.gameFaqId,
gameCharClasses: g.gameCharClasses
};
});
export default function makeData(lens = [2]) {
const makeDataLevel = (depth = 0) => {
const len = lens[depth]
return range(len).map(d => {
return {
...gameListing(),
subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
}
})
}
return makeDataLevel()
}
The problem is, after hours of debugging, I can't get it to work.
My latest issue is that it's not displaying any data.
Here is a Code Sandbox: https://codesandbox.io/s/trusting-booth-b7wxg
I see one problem is youre using .map wrong
you have to pass in a parameter and that parameter is basically like a placeholder to use that has the data within it
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
for example:
const gameListing = data.map((elem) => {
return {
id: elem.id,
gameTitle: elem.gameTitle,
startDate: elem.startDate,
endDate: elem.endDate,
description: elem.description,
gameUrl: elem.gameUrl,
isOpen: elem.isOpen,
gameFaqId: elem.gameFaqId,
gameCharClasses: elem.gameCharClasses
}
})
Does that help or work?

Resources