How to Set States Inside Loop React Nextjs - reactjs

I have a functions that should update data provided by the user. But due to some strange reason its only updating first value of quizzes. Its not updating Topic value and when adding second value to quizzes it revert backs to 1st value.
Data
[
{
"Topic": [ "Biology", "Introduction to Biology" ],
"Question": "Example of micro molecule is:",
"Quizzes" : ["2022 Biology Geek", "General Knowledge"]
},
{
"Question": "Pepsin enzyme works in;"
}
]
Code
const [finalData, setFinalData] = useState({
TOPIC: [],
QUIZZES: []
})
const setData = () => {
const ei = userSelectedData.topic;
const yr = userSelectedData.quiz
selectedDocsID.forEach(doc => {
fetchDoc("ques", doc)
.then((response) => {
const TField = response["Topic"]
const QField = response["Quizzes"]
const newTs = new Set([...finalData.TOPIC]);
const newQs = new Set([...finalData.QUIZZES]);
//If Topic Field Does not Exist
if (TField == undefined) {
newTs.add(doc);
setFinalData({ ...finalData, EI: Array.from(newTs) })
}
//If Topic Field Exist But Don't have Requried Value
else if (TField.includes(ei) != true) {
newTs.add(doc);
setFinalData({ ...finalData, EI: Array.from(newTs) })
}
//If Quiz Field Does not Exist
if (QField == undefined) {
newQs.add(doc);
setFinalData({ ...finalData, YR: Array.from(newQs) })
}
//If Quiz Field Exist But Don't have Requried Value
else if (QField.includes(yr) != true) {
newQs.add(doc);
setFinalData({ ...finalData, YR: Array.from(newQs) })
}
})
});
}
<Button onClick={() => setData()}>Update Data</Button>
Current Result
//Displaying Results by console.log inside useEffects Hook
{
"TOPIC": [],
"QUIZZES": [
"2022 Biology Geek",
"General Knowledge"
]
}
//After Few Seconds
{
"TOPIC": [],
"QUIZZES": [
"2022 Biology Geek"
]
}
Expected Result
{
"TOPIC": ["Biology"],
"QUIZZES": [
"2022 Biology Geek",
"General Knowledge"
]
}

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

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

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

How to transform object to array before parsing in Zod

I do have an external URL endpoint that returns an array of field object when it is more than 2 and an object when there is only one, see the snippet below:
Return when the field count is one:
{
"fields": { "fullName": "fieldFullname", "type": "fieldType" }
}
Return when the field is more than one:
{
"fields": [
{ "fullName": "fieldFullname", "type": "fieldType" },
{ "fullName": "fieldFullname", "type": "fieldType" }
]
}
Currently, this is my schema using zod:
export const sObjectMetadataSchema = z.object({
fields: z.array(metadataFieldSchema).optional()
});
export const metadataFieldSchema = z.object({
fullName: z.string().optional(),
type: z.string().optional(),
});
It is configured that it will only accept an array of objects. When it returns only one field it throws an error:
{
"code": "invalid_type",
"expected": "array",
"received": "object",
"path": [],
"message": "Expected array, received object"
}
My goal is if it returns a single object it will convert it to an array of objects during runtime. Currently trying to implement using transform but still not working:
An initial implementation using transform:
export const sObjectMetadataSchema = z.object({
fields: z.unknown().transform((rel) => {
return Array.isArray(rel)
? z.array(metadataFieldSchema).optional()
: 'Convert the rel to Array?';
}),
});
I didn't test that, but seems like should work:
const FieldsSchema = z.object({
fullName: z.string(),
type: z.string()
});
export const sObjectMetadataSchema = z.object({
fields: z.union([FieldsSchema, FieldsSchema.array()]).transform((rel) => {
return Array.isArray(rel)
? rel
: [rel];
}),
});
Thanks for the answer #Konrad !
I improved it a little bit in typescript, so it is also typed correctly:
const arrayFromString = <T extends ZodTypeAny>(schema: T) => {
return z.preprocess((obj) => {
if (Array.isArray(obj)) {
return obj;
} else if (typeof obj === "string") {
return obj.split(",");
} else {
return [];
}
}, z.array(schema));
};

How to push an object to the dict state?

I'm trying to push an object to the state.
I created a function for this. This function should copy the first state object and add it to the end of the state.
addField() {
const index = (this.state?.fields.length) -1
const newFields = [...this.state?.fields[index]]
console.log("newFields", newFields) // [ { "key": "input_field_name", "value": "This is a value 4" }, { "key": "field_name", "value": "field name's value 4" }, { "key": "datatype", "value": "text" }, { "key": "Datatype", "value": "Label 4" }]
this.setState(
{
fields: [[...this.state.fields[index]], newFields],
},
() => {
console.log("updated state", this.state);
}
);
console.log("state: ", this.state)
}
This is my state:
fields: IFieldDefinition[][];
activeFields: {
key: number;
fields: IFieldDefinition[];
};
Updated code:
Now, when I run the function, it creates a new state from the last object of the state instead of add.
how can I fix that?
fields is an array, so you cannot assign values as an object with {}
If you want to add that new state to the end of the state, you can use the below approach
const index = (this.state?.fields.length) - 1
const newFields = [...this.state?.fields[index]];
//if the current index is matched with which field you want to update
this.setState(
{
fields: this.state.fields.map((currentValue, currentIndex) =>
currentIndex === index ? [...currentValue, ...newFields] : currentValue
),
},
() => {
console.log("updated state", this.state);
}
);
console.log("state: ", this.state);
Do you want to remove the 0th index data and move it to the end of the array
if so
let a={A:[{a:1},{b:2},{c:3}]}
let temp=a.A.shift()
a.A.push(temp)
setState({...a})

Find through array in array field

How can I find something through some arrays that also contain an array?
To be more precisely:
And I want to return from the coaches array, the id(within the coaches) that matches the username. What I've tried:
if (!(args['coach'].value === '') && (args['coach'].value !== null)) {
coachId = this.items.find(x => x.username === args.coach.value).id;
}
Basically this.items is what I've console.log before. Now it gives me undefined.
Has someone a fix for this? Thank you very much.
[
{
"id":584,
"name":"Name",
"coaches":[
{
"id":8587,
"username":"test"
},
{
"id":8589,
"username":"test1"
}
]
},
{
"id":587,
"name":"O1",
"coaches":[
]
}
]
And let s say I want to return the id 8587 when searching for the name test.
Combine map and find:
const array = [
[{a1: 1},
{a2: 2}
],
[{a1: 1},
{a2: 2},
{a3: 3}]
];
const element = array.map(innerArray => {
const found = innerArray.find(el => el.a1 === 1);
if (found) return found.a1;
return null;
}).find(el => el !== null);
console.log(element) // 1
For finding multiple matches do as follows:
const data = [{
"id": 584,
"name": "Name",
"coaches": [{
"id": 8587,
"username": "test"
},
{
"id": 8589,
"username": "test1"
}
]
},
{
"id": 587,
"name": "O1",
"coaches": [
]
}
];
const usernameToSearch = 'test1';
const foundCoachIds = data
.reduce((acc, curr) => {
// Destructure the coaches property first
const {
coaches,
...rest
} = curr;
// Check if any username matches the coach
const foundMatches = coaches.filter(x => x.username === usernameToSearch);
// If there is any found match push into accumulator
if (foundMatches.length) {
for (const foundMatch of foundMatches) {
if (acc.indexOf(foundMatch.id) === -1) {
acc.push(foundMatch.id);
}
}
}
return acc;
}, []);
console.log(foundCoachIds);
let y = this.items.filter(o => o.coaches.some(e => e.username === 'test'))[0]
.coaches.filter(e=>e.username === 'test')[0].id;
console.log(y);
const data = [
{
"id":584,
"name":"Name",
"coaches":[
{
"id":8587,
"username":"test"
},
{
"id":8589,
"username":"test1"
}
]
},
{
"id":587,
"name":"O1",
"coaches":[
]
}
];
For outer id
data.map(it => {return !!it.coaches.find(it2 => it2.username == "test") ? it.id : null}).filter(it=>!!it)
evaluates to [584]
For inner
(coaches) id:
data.map(it => it.coaches.find(it2 => it2.username == "test")).filter(it => !!it).map(it=> it.id)
returns [8587]
Just need to take the first item from these to get your answer.

Resources