Related
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
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
I have an array of objects response that comes from API. I don't have access to the API to be able to change it.
data = [
{"label": "Group 1", "value": "1"},
{"label": "Group 2", "value": "2"},
{"label": "Group 3", "value": "3"}
]
I need to display the data, so the label says Post to ${label}.
Can I inject the 'Post to ' string somehow? Do I make a new array from my response?
Here's how I'm fetching the data:
const [pickerData, setPickerData] = React.useState<{ label: string; value: string }[]>([]);
const fetchPickerData = React.useCallback(async () => {
const response = await getPostLocations();
if (!response) return;
if (response) {
setPickerData(response);
}
}, []);
React.useEffect(() => {
fetchPickerData().catch(console.error);
}, [fetchPickerData]);```
and my data is then loaded into a picker:
<Picker
items={pickerData}
value={pickerItem}
onValueChange={(pickerItem) => setPickerItem(pickerItem)}
/>
Things I already tried is trying to inject string into value like value={()=>{`Post to ${pickerItem}`}}, or the same in the setPickerItem in onValueChange but that didn't work
You can use Array#map() to create a new array from the response in the format you prefer:
const fetchPickerData = React.useCallback(async () => {
const response = await getPostLocations();
if (!response) return;
if (response) {
const formattedResponse = response.map(
({ label, value }) => ({ 'label': `Post to ${label}`, value })
)
setPickerData(formattedResponse);
}
}, []);
Try it like this
const [pickerData, setPickerData] = React.useState<{ label: string; value: string }[]>([]);
const [apiData, setApiData] = React.useState<{ label: string; value: string }[]>([]);
const fetchPickerData = React.useCallback(async () => {
const response = await getPostLocations();
if (!response) return;
if (response) {
setApiData(response);
}
}, []);
React.useEffect(() => {
fetchPickerData().catch(console.error);
}, [fetchPickerData]);
React.useEffect(() => {
if (apiData.length > 0) {
let Temp = [];
apiData.forEach((item) => {
let newItem = {
...item,
label: `Post to ${item.label}`
};
Temp.push(newItem);
});
setPickerData(Temp);
}
}, [apiData]);
I'm new to typescript and facing a problem that I'm not able to solve by myself.
First I'm fetching data from API:
/* data.utils.ts */
export const getCompany = async<T> (url: string): Promise<T> =>{
const response = await fetch(url);
return await response.json();
}
Then I use it in the Company page component:
/* Company.tsx */
import { getCompany } from '../../../utils/data.utils';
type TCompany = {
Name: string;
Description: string;
Address: string;
MarketCapitalization: string;
};
const Company = () => {
const [companyData, setCompanyData] = useState<TCompany[]>([]);
let { companySymbol } = useParams();
useEffect(() => {
const fetchCompany = async () => {
const url = `https://www.alphavantage.co/query?function=OVERVIEW&symbol=${companySymbol}&apikey=*******`;
const fetchedData = await getCompany<TCompany[]>(URL);
setCompanyData(fetchedData);
};
fetchCompany();
}, []);
return (
<>
<div>{companyData.Name}</div> //Property 'Name' does not exist on type 'TCompany[]'.ts(2339)
</>
);
};
export default Company;
But <div>{companyData.Name}</div> gives me a TS error Property 'Name' does not exist on type 'TCompany[]'.ts(2339)
the output from API:
{
"Symbol": "IBN",
"AssetType": "Common Stock",
"Name": "ICICI Bank Limited",
"Description": "ICICI Bank Limited offers various banking products and financial services in India and internationally. The company is headquartered in Mumbai, India.",
"CIK": "None",
"Exchange": "NYSE",
"Currency": "USD",
"Country": "USA",
"Sector": "TECHNOLOGY",
"Industry": "GENERAL",
"Address": "NONE",
"FiscalYearEnd": "March",
"LatestQuarter": "2021-12-31",
"MarketCapitalization": "68983734000",
"EBITDA": "None",
"PERatio": "23.73",
"PEGRatio": "0",
"BookValue": "504.33",
"DividendPerShare": "0",
"DividendYield": "0.0028",
"EPS": "0.824",
"RevenuePerShareTTM": "301.8",
"ProfitMargin": "0.22",
"OperatingMarginTTM": "0.249",
"ReturnOnAssetsTTM": "0.0149",
"ReturnOnEquityTTM": "0.139",
"RevenueTTM": "1014660530000",
"GrossProfitTTM": "966356348000",
"DilutedEPSTTM": "0.824",
"QuarterlyEarningsGrowthYOY": "0.175",
"QuarterlyRevenueGrowthYOY": "0.001",
"AnalystTargetPrice": "26.87",
"TrailingPE": "23.73",
"ForwardPE": "19.61",
"PriceToSalesRatioTTM": "0.068",
"PriceToBookRatio": "2.975",
"EVToRevenue": "-",
"EVToEBITDA": "-",
"Beta": "1.032",
"52WeekHigh": "22.34",
"52WeekLow": "14.95",
"50DayMovingAverage": "19.22",
"200DayMovingAverage": "19.5",
"SharesOutstanding": "3474310000",
"DividendDate": "2021-09-03",
"ExDividendDate": "2021-07-28"
}
How should I write the TCompany to match the types correctly??
Giving the Type any to the useState hook is not an option.
Any ideas what did go wrong here?
companyData is an array so you should be accessing it with indexes.
Example:
companyData[0].Name
Or you should define companyData not as an array but as an object
const [companyData, setCompanyData] = useState<TCompany>({});
And then
const fetchedData = await getCompany<TCompany>(URL);
I am trying to update the object inside the document
Document: Cats
{
"_id": "5e5cb512e90bd40017385305",
"type": "cat"
"history": [
{
"id": "randomID",
"content": "xyz",
},
{
"id": "randomID2",
"content": "abc",
}
]
}
Code to select and update the object inside the history array:
const editHistory = async (_, { input }, ctx) => {
let query = { _id: input.catId, "history.id": input.historyId };
let update = { $set: { "history.$": input.history } };
let options = {
new: true,
fields: { history: { $elemMatch: { id: "randomID" } } }
};
let cat = await ctx.models.cats.findOneAndUpdate(query, update, options);
return cat;
};
Input has following values
input: {
catId: "5e5cb512e90bd40017385305",
historyId: "randomID",
history: {
id: "randomID",
content: "new content"
}}
I tried using Projection, I used select changed it to field, found in mongoose documentation.
I still couldn't update the values. Is there anything wrong with the way i am querying or selecting the subfield.
Found the Solution for it by going through more detail of the operator($set) and option(new, fields).
Question:
const editHistory = async (_, { input }, ctx) => {
let query = { _id: input.catId, "history.id": input.historyId };
let update = { $set: { "history.$": input.history } };
let options = {
// using new option would return the new document
new: true,
/* using fields option would select the based on the given key, but using new:
true with fields will throw error: 'cannot use a positional projection and
return the new document'
*/
fields: { history: { $elemMatch: { id: "randomID" } } }
};
let cat = await ctx.models.cats.findOneAndUpdate(query, update, options);
return cat;
};
This post below answers that question for *error: 'cannot use a positional projection and return the new document'.
https://stackoverflow.com/a/46064082/5492398
Final Solution:
const editHistory = async (_, { input }, ctx) => {
let query = { _id: input.catId, "history.id": input.historyId };
let update = { $set: { "history.$": input.history } };
let options = {
new: true
};
let cat = await ctx.models.cats.findOneAndUpdate(query, update, options);
return cat;
};
Removing field option, since I don't need the unmodified selection before atomic modification, solves the question.