Query regarding looping a response data array using async await - javascript

I am working with an existing class components based React application and was dumbfounded when I spent almost 3 hours to figure out the root cause of the following code snippet.
In componentDidMount which is async btw, I am calling backend API designed with Node.js using axios like this
(Note state below is in Class Component which I skipped for obvious reasons)
state = {
grades = [];
}
try {
const res = await axios.get(
`/assignments/getStudentAssignment/${this.props.studentId}`
);
const assignments = res.data.assignment.assignments;
assignments.forEach((assignment) => {
this.setState({grades: [...this.state.grades, assignment.grade]})
}))
} catch (err) {
'some logic'
}
Where res.data.assignment.assignments is an array of each student assignments, and from each assignment I am pushing a new grade in grades array state. In console.log using Chrome browser I saw that the grades array had 8 elements but I couldn't view them in console.log, and the length of array was also set to 0.
I consoled assignment within forEach array and also set a counter in order to see that is forEach looping through the entire array or just first element. Console.log only printed first counter number and also first array element
I figured out that since the assignments is an array of promises that are yet to be resolved, I cannot loop through the entire array, and then I changed my code to this
await assignments.forEach((assignment) => {
this.setState({grades: [...this.state.grades, assignment.grade]})
}))
And viola, it worked like a charm but I am confused on a couple of things.
Isn't await supposed to resolve promise res.data.assignment.assignments,
meaning I can loop through resolved array data
Why did forEach work for only first element of
res.data.assignment.assignments if the array was yet to be resolved?
I tried on MDN, by creating a sample array in a promise and then looping through the contents of array in a then chain where each element was printed on console perfectly. Any help will be appreciated
EDIT
I have changed the original code, axios returns an assignment object which also contains an array of assignments
I worked with the following code once again without await, and its working fine. This is the second time, I had trouble with React setState, first time setState wasn't firing for some reason, and then after deleting node_modules and installing all required packages it worked, even though it wasted a day
state = {
grades = [];
}
try {
const res = await axios.get(
`/assignments/getStudentAssignment/${this.props.studentId}`
);
const assignments = res.data.assignment.assignments;
assignments.forEach((assignment) => {
this.setState({grades: [...this.state.grades, assignment.grade]})
}))
} catch (err) {
'some logic'
}

Related

React setState, using array methods in callback function

I am currently following a React course on Scrimba on creating a web app for taking notes.
The problem requires me to bump a note to the top of the note list every time it's updated.
The notes are initialised through useState as follows:
const [notes, setNotes] = useState([])
The array consists of the individual notes as objects with an id and body
Every time an onChange is triggered, the following function is ran:
function updateNote(text) {
setNotes(oldNotes => {
let updated = oldNotes.map(oldNote => {
return oldNote.id === currentNoteId
? { ...oldNote, body: text }
: oldNote
})
const currNoteIndex = updated.findIndex(
note => note.id === currentNoteId
)
console.log(currNoteIndex)
updated.unshift(updated.splice(currNoteIndex, 1))
return updated
})
}
However, I keep getting an error as shown in the image.
It's very unclear to me where the problem lies, but I'm thinking it has to do with the array methods.
Any explanation for this issue would be greatly appreciated!
Credits to jsN00b for the answer:
array.splice returns an array, not the object.
Since that array is inserted to the start of the array containing the objects, there will be an error when updateNote() is called again.

variable inside Observable subscription gets empty value

I know that Observables take some time to get data while javascript keeps running the others codes and that is troubling me a lot.
I have used ngrx in my angular project. Here, I am trying to fetch some data from the store which is working fine. Then, I convert this data stream into string[] which is also working fine.
To use this string[] me subscribeto this observable. And inside subscription I try to assign the value to other values named filterSizeValues.
Here, the problem comes. If I console.logthis filterSizeValuesinitially I got and empty array. When the observable finishes his job filterSizeValues variable is filled with data.
But I can not effort filterSizeValues variable to be empty array initially. What can I do?
I have already searched the solution in the internet but nothing is working out.
Help me out please. And Many Many Thanks in advance.
Here is my code;
this.sizeTargetingStore$.dispatch(SizeTargetingActions.getSizeTargeting({
campaignId: this.campaignId,
lineItemId: this.lineItemId
}));
Here I am accessing the store to get data.
this.sizeTargeting$
.pipe(switchMap(sizes=>{
let temporary:string[] = [];
sizes.forEach(eachSize=>{
temporary.push(eachSize.name);
})
this.filterSizeValues$ = of(temporary);
return this.filterSizeValues$;
}))
.subscribe(size_name=>{
this.filters.set('size_name', size_name);
})
Here, I am trying to set the filter values.
I also tried this way also.
this.sizeTargeting$
.pipe(switchMap(sizes=>{
let temporary:string[] = [];
sizes.forEach(eachSize=>{
temporary.push(eachSize.name);
})
this.filterSizeValues$ = of(temporary);
return this.filterSizeValues$;
}))
.subscribe(size_name=>{
this.filterSizeValues = size_name
})
this.filters.set('size_name', this.filterSizeValues);
But all ways filters set to an empty array.
Anyone can help me out please?
From my understanding, you have 2 possibilities, either filter out the empty values or skip the first value. You can do so with the filter and skip rxjs operator respectively.
Also I believe that you are misusing the switchMap operator, since you are not using asynchronous operations within your switchMap we can use the map operator instead, so below I have a simplified version of your code with your 2 options to fix your problem.
Option 1:
this.sizeTargeting$.pipe(
filter(sizes => sizes.length > 0), // filter out empty array values
map(sizes => sizes.map(size => size.name)) // perform your remap
).subscribe(sizes => {
this.filterSizeValues = size_name; // Only arrays with values will reach this step
});
Option 2:
this.sizeTargeting$.pipe(
skip(1), // skip the first value
map(sizes => sizes.map(size => size.name)) // perform your remap
).subscribe(sizes => {
this.filterSizeValues = size_name; // Only arrays with values will reach this step
});
Normally when I subscribe to something that I am waiting on to return what I do is I set up a Subject:
private componentDestroyed$ = new Subject<void>();
then in the Observable piping and subscription I do it as:
this.sizeTargeting$
.pipe(takeUntil(this.componentDestroyed$))
.subscribe((sizes: YourTypeHere[]) => {
if(sizes) {
//Do what I need to do with my sizes here, populate what I need,
//dispatch any other actions needed.
}
})

Apollo/GraphQL/React, How to query for data in a loop?

In one of my React components, I have an array that includes a number of IDs as Strings. I want to query each ID in the array and return an array of its associated objects within my MongoDB database. However, this only happens conditionally, so I wanted to use useLazyQuery instead of useQuery.
I tried to accomplish it via mapping, but it seems I had an issue with using useLazyQuery. Here's the issue:
const [queryId, {loading, data}] = useLazyQuery[QUERY_ID];
if (queryForIds) {
// Using just the first ID
(queryId({variables: {id: idList[0]}}));
console.log(data);
}
This results in an infinite loop of nothing being printed (maybe my resolver returns nothing but regardless I get an infinite loop that crashes my site). I am most likely misusing useLazyQuery but I'm unsure of how.
Originally my idea was to do this:
const [queryId, {loading, data}] = useLazyQuery[QUERY_ID];
if (queryForIds) {
// Using just the first ID
idList.map((id) => (queryId({variables: {id: idList[0]}}))).data);
}
But I'm also unsure if that works either. How can I resolve either issue?

Gatsby source plugin only showing last item in array in GraphQL

When I console log after I run build 6 objects show up in my drinks array. They also show up when I run develop. But when I query graphQL only the last object in my array is available. I am new to gatsby and graphQL so included image just in case my query was off.
Code is from my gatsby-node.js file:
exports.sourceNodes = async (
{ actions, createContentDigest, createNodeId,}
) => {
const NODE_TYPE = "CocktailRecipes";
try{
const response = await fetch(`https://www.thecocktaildb.com/api/json/v1/1/search.php?s=vodka`)
const data = await response.json();
const {drinks} = data;
console.log(drinks)
drinks.forEach((drink) => {
actions.createNode({
...drink,
id: createNodeId(`${NODE_TYPE }-${drink.id}`),
parent:null,
children:[],
internal:{
type:NODE_TYPE,
content:JSON.stringify(drink),
contentDigest: createContentDigest(drink)
}
})
})
}
catch(error){
console.log("ERROR")
console.log(error)
}
}
only one object showing in graphQL
If anyone could help it would be very much appreciated as I've been banging my head on a wall for awhile now. I've done a gatsby clean. I've tried map instead of forEach
I've faced exactly the same issue as you a few months ago and, in my case was that I needed to set a valid internal id for each element to allow GraphQL to create a proper schema for each node if not, the id is overridden in each element and it only takes the last one.
In your case, it seems that some field is wrong, making the following expression invalid:
id: createNodeId(`${NODE_TYPE }-${drink.id}`),
Try debugging more what's receiving and changing it to some hardcoded value. Something like:
id: drink.id,
Keep in mind that, if the ids are different for each node, you don't need to use createNodeId API for debugging purposes (but it's recommended).

Why does this Promise.all() function only return the last resolved value?

I'm trying to make a persistent shopping cart on React Native. It works if you only have one item added, but if you add another one, all of the items become the same as the last one you added. I narrowed it down to our getData() function, which returns the data of the products that we render.
async getData(cartItems){
let promises = []
cartItems.forEach(item => {
promises.push(productInfo(item.id))
})
return Promise.all(promises)
}
And productInfo() looks like this.
export function productInfo(product_id){
vapeBluntPOST.url = baseUrl+'/api/productinfo'
vapeBluntPOST.data.id = product_id
return axios(vapeBluntPOST).then(({data})=>(data))
}
The problem seems to be that the elements of the array returned by Promise.all() are all the same as the last resolved value corresponding to the last promise.
The problem is with vapeBluntPOST, which is not locally scoped within productInfo. So by the time the promises resolve, they are all using that from the last call, which comes from the last id in the array.
Assuming you don't actually need that object elsewhere, the easy fix is to just use a local variable instead:
export function productInfo(product_id){
const vapeBluntPOST = { /* common configuration that you need */};
vapeBluntPOST.url = baseUrl+'/api/productinfo'
vapeBluntPOST.data.id = product_id
return axios(vapeBluntPOST).then(({data})=>(data))
}

Categories

Resources