Array being filled in inverse in React - javascript

I am trying to fill an array with several array of objects, but on first render, the values are being filled in inverse inside the array and after refreshing they go back to how they should be.
const getProducts = async (data) => {
await productServices
.getProductsByTakeoffWizardStep(data)
.then((response) => {
setRows((rows) => [...rows, response.data]);
})
.catch((error) => {
console.log(error.response);
});
};
const getTakeoffIDs = () => {
var i = 1;
takeoffServices
.getTakeoffSteps(5)
.then((response) => {
response.data.forEach((element) => {
setSteps((steps) => [...steps, element.description]);
setStepsID((stepsID) => [...stepsID, element.stepID]);
var data = { StepID: element.stepID, TakeoffID: 4 };
getProducts(data);
setStepSequence((stepSequence) => [
...stepSequence,
"Step " + i + "",
]);
i = i + 1;
});
})
.catch((error) => {
console.log(error.response);
});
};
useEffect(() => {
setSteps([]);
setStepsID([]);
setRows([]);
getTakeoffIDs();
}, []);
So on first render the array looks like this
(2) [Array(1), Array(2)]
0: Array(1)
0: {product: {…}, quantity: null}
length: 1
[[Prototype]]: Array(0)
1: Array(2)
0: {product: {…}, quantity: null}
1: {product: {…}, quantity: null}
length: 2
[[Prototype]]: Array(0)
length: 2
[[Prototype]]: Array(0)
and after refreshing the page it looks like this
(2) [Array(2), Array(1)]
0: Array(2)
0: {product: {…}, quantity: null}
1: {product: {…}, quantity: null}
length: 2
[[Prototype]]: Array(0)
1: Array(1)
0: {product: {…}, quantity: null}
length: 1
[[Prototype]]: Array(0)
length: 2
[[Prototype]]: Array(0)
What could be causing this and what can I do to fix it?
I am accessing the page using history.push() from another page but none of the states I am passing affects the fetching process, only the display of some paragraphs not related to the data I am fetching.

This is how I would write this:
const getProduct = async (data) => {
try {
const response = await productServices.getProductsByTakeoffWizardStep(data);
return response.data;
} catch (error) {
console.log(error.response);
}
}
const getTakeoffIDs = () => {
takeoffServices
.getTakeoffSteps(5)
.then(async (response) => {
// try moving setSteps(), setStepsID() and setStepSequence() up here, so they don't wait for the products.
const products = await Promise.all(
response.data.map(element => getProduct({
StepID: element.stepID,
TakeoffID: 4
}))
);
setRows((rows) => [
...rows,
...products
]);
setSteps((steps) => [
...steps,
...response.data.map(element => element.description)
]);
setStepsID((stepsID) => [
...stepsID,
...response.data.map(element => element.stepID)
]);
setStepSequence((stepSequence) => [
...stepSequence,
...response.data((_, i) => `Step ${i + 1}`)
]);
})
.catch((error) => {
console.log(error.response);
});
};
useEffect(() => {
setSteps([]);
setStepsID([]);
setRows([]);
getTakeoffIDs();
}, []);
Instead of calling all these setState()s multiple times I'd Array.map() the data and add it all at once.
await for all the getProduct() calls to finish and also push them at once; in order.
The one thing I'm not sure about is if you want the setRows() to be called together with the other setter, or if the other ones should go first (because the data is available) and setRows() should be called later, as soon as the data is available.

Related

Modify array in observable with rxjs, return whole object

I have observable like this:
currentPage: 1
items: (6) [{…}, {…}, {…}, {…}, {…}, {…}]
pageSize: 10
totalItems: 6
totalPages: 1
I'm trying to modify every element in items array, and then return a whole object.
getAllGifts(): Observable<PagedListDTO<GiftIdeaDTO>> {
return this.http.get<PagedListDTO<GiftIdeaDTO>>(this.urlAPI + '/GiftIdeas/GetAll').
pipe(
map(x => x.items.map
(y => ({ ...y, imageContentB64: y.imageContentB64 + 'Bob' })))
,
tap(console.log)
);
}
but i only get a modified array of items, no currentPage property, page Size etc.
How can i modify items array and return whole object?
You seem to be already familiar with the usage of spread syntax (...). You could also use it for the outer object before applying the Array#map to the items property.
Try the following
getAllGifts(): Observable<PagedListDTO<GiftIdeaDTO>> {
return this.http
.get<PagedListDTO<GiftIdeaDTO>>(this.urlAPI + '/GiftIdeas/GetAll')
.pipe(
map(ideas => ({
...ideas, // retain properties of the outer object
items: ideas.items.map(item => ({ // adjust the `items` property
...item,
imageContentB64: item.imageContentB64 + 'Bob'
}))
})),
tap(console.log)
);
}
The map(x => only accounts for x.items and misses the rest of the props.
This should fix it:
getAllGifts(): Observable<PagedListDTO<GiftIdeaDTO>> {
return this.http.get<PagedListDTO<GiftIdeaDTO>>(this.urlAPI + '/GiftIdeas/GetAll').
pipe(
map(x => ({...x, items: x.items.map
(y => ({ ...y, imageContentB64: y.imageContentB64 + 'Bob' })))
}),
tap(console.log)
);
}
In the above code, x is mapped to include all props and then items is updated using x.items.map.
You are not returing them, use this:
getAllGifts(): Observable<PagedListDTO<GiftIdeaDTO>> {
return this.http.get<PagedListDTO<GiftIdeaDTO>>(this.urlAPI + '/GiftIdeas/GetAll').
pipe(
map(x => {
return {
...x,
items: x.items.map(y => ({ ...y, imageContentB64: y.imageContentB64 + 'Bob' }))
}
})
)
,tap(console.log)
);
}

use another useeffect result in another

New react developer here, here i have two useEffects, one of them(first one) is an object which contains a 'name' which i want to use in my second useEffect. Second useEffect contains an array of objects, these array of objects has 'billingName' and 'driverName', some of them has same value in them for example driverName: "james". What i want to do is in my second useEffect check if 'name' from first useEffect is same as 'driverName', only then do this 'setOrders(res);
setRenderedData(res);'
my error message: Property 'driverName' does not exist on type...
my object: {
id: "98s7faf",
isAdmin: true,
name: "james"}
my array:  [{billingName: "trump",driverName: "james"}, {billingName: "putin",driverName: "alex"}, {…}, {…}, {…}]
my code:
const [driver, setDriver] = useState<User>();
useEffect(() => {
api.userApi
.apiUserGet()
.then((res: React.SetStateAction<User | undefined>) => {
console.log(res);
setDriver(res);
});
}, [api.userApi]);
useEffect(() => {
api.caApi
.apiCaGet(request)
.then((res: React.SetStateAction<CaDto[] | undefined>) => {
if (driver?.name == res?.driverName) {
setOrders(res);
setRenderedData(res);
console.log(res);
}
});
}, [api.caApi]);
You can call api.caApi in then of api.userApi,
useEffect(() => {
api.userApi.apiUserGet().then((res1?: User ) => {
setDriver(res1);
return api.caApi.apiCaGet(request).then((res?: CaDto[])
=> {
if (res.some(({ driverName }) => driverName === res1?.name)) {
setOrders(res);
setRenderedData(res);
console.log(res);
}
});
});
}, [api.caApi, api.userApi]);

Turn a for loop into a foreach loop not working

I am trying to turn a for loop into a forEach loop, but it doesn't seem to be working...
Here is my code:
const townDataURL = "[some link I probably can't disclose...]"
const towns2get = [
"Preston",
"Fish Haven",
"Soda Springs"
]
fetch(townDataURL)
.then((response) => {
return response.json()
})
.then((jsonData) => {
const towns = jsonData["towns"].filter((item) => {
// for (let i = 0; i<towns2get.length; i++) {
// if (item.name == towns2get[i]) {
// return item
// }
// }
return (towns2get.forEach(elem => {
return ( (item.name == elem) ? (item) : "Hello?" )
}))
})
console.log(towns)
})
When I have the commented code run it gives me this:
(3) [{…}, {…}, {…}]
0: {name: "Fish Haven", photo: "fishhaven.jpg", motto: "This is Fish Heaven.", yearFounded: 1864, currentPopulation: 501, …}
1: {name: "Preston", photo: "preston.jpg", motto: "Home of Napoleon Dynamite.", yearFounded: 1866, currentPopulation: 5204, …}
2: {name: "Soda Springs", photo: "sodasprings.jpg", motto: "Historic Oregon Trail Oasis. The Soda is on Us.", yearFounded: 1858, currentPopulation: 2985, …}
length: 3
__proto__: Array(0)
Which is exactly what I want, but I want to simplify my code... what I have now gives me this:
[]
length: 0
__proto__: Array(0)
I've done some debugging and I know that my conditional statement with the ternary operator works fine, and it is returning a value... but I can't seem to figure out why it isn't returning it back to the filter method...
Does it not work this way? Or do I have to somehow put the forEach with the filter?
Thank you for any help!
The better way here is to use the .includes function inside the filter method
const townDataURL = "[some link I probably can't disclose...]"
const towns2get = [
"Preston",
"Fish Haven",
"Soda Springs"
]
fetch(townDataURL)
.then((response) => {
return response.json()
})
.then((jsonData) => {
const towns = jsonData["towns"].filter((item) => {
if(towns2get.includes(item.name) > -1) return true;
else return false;
})
console.log(towns)
})
Cris G gave me the answer:
.forEach() doesn't return anything, so your .filter() callback returns undefined, a falsey value, for each element. Use filter(item => towns2get.includes(item.name)) – Chris G
So the code should then be:
const townDataURL = "[some link I probably can't disclose...]"
const towns2get = [
"Preston",
"Fish Haven",
"Soda Springs"
]
fetch(townDataURL)
.then((response) => {
return response.json()
})
.then((jsonData) => {
const towns = jsonData["towns"].filter(item => towns2get.includes(item.name))
console.log(towns)
})

How to filter out array within array of objects based on date range?

I have an array of objects, each element looking something like this:
campaigns: (5) ["get.dentalintel.net/call-insight-DeshaunWatson", "get.dentalintel.net/practice-analysis-DeshaunWatson", "get.dentalintel.net/morning-huddle-DeshaunWatson", "get.dentalintel.net/new-morning-huddle-DeshaunWatson", "get.dentalintel.net/followups-DeshaunWatson"]
email: "dwats#gmail.com"
name: "Deshaun Watson"
submissions: [{…}]
user_id: 166
within each element there is an array of submission objects that appear like this:
campaigns: (5) ["get.dentalintel.net/call-insight-DeshaunWatson", "get.dentalintel.net/practice-analysis-DeshaunWatson", "get.dentalintel.net/morning-huddle-DeshaunWatson", "get.dentalintel.net/new-morning-huddle-DeshaunWatson", "get.dentalintel.net/followups-DeshaunWatson"]
email: "dwats#gmail.com"
name: "Deshaun Watson"
submissions: Array(1)
0:
addedAt: 1574185321138
canonical-vid: 13476551
form-submissions: []
identity-profiles: [{…}]
is-contact: true
merge-audits: []
merged-vids: []
portal-id: 2271480
profile-token: "AO_T-mOo4xVvqmFyVkiizX8l0pCPZTakJOfo02uTm2kWzr68fzpXI6-xmyh4Gmj_Pzpp8IBDdbEN9CCRW4GeMfybZaSBiMZ8xXo2U2dylZ7QD3CufR-ERrazbZlKPaDyVzxaCqwvXU3W"
profile-url: "https://app.hubspot.com/contacts/2271480/contact/13476551"
properties: {partner_email: {…}, utm_campaign: {…}, lastmodifieddate: {…}, partner_last_name: {…}, email: {…}}
vid: 13476551
__proto__: Object
length: 1
__proto__: Array(0)
user_id: 166
The only property of note on this is the addedAt value.
I want to filter out submission objects based on a date range. This is what I have tried so far with no luck:
filterByDate = () => {
const { partners, endDate, startDate } = this.state;
partners.forEach(partner => {
for (let i = 0; i < partner.submissions.length; i++) {
if (
moment(partner.submissions[i].addedAt).isBetween(startDate, endDate)
) {
return partners;
} else {
return partner.submissions.splice(i, 1);
}
}
});
};
It removes some of the submission objects, but never the correct ones. Any suggestions on a better approach would be appreciated.
For filtering use Array.filter. This returns a new array with only the values that meet the boolean returned from the callback. For creating a new array from existing arrays use Array.map instead of Array.forEach. forEach loop only iterates over the values but Array.map returns a modified or new value for every element.
const filterByDate = () => {
const { partners, endDate, startDate } = this.state;
const filteredPartners = partners.map(partner => {
return {
...partner,
submissions: partner.submissions.filter(submission =>
moment(submission.addedAt).isBetween(startDate, endDate)
)
};
});
};
Example Array.filter and Array.map (No mutation)
const testArray = [{
id: 1
}, {
id: 2
}, {
id: 3
}, {
id: 4
}]
const filtered = testArray.filter(e => e.id !== 3)
const mapped = testArray.map(e => {
return {
id: e.id,
name: `Name of ${e.id}`
}
})
console.log("Test Array: ", testArray)
console.log("Filtered Array: ", filtered)
console.log("Mapped Array: ", mapped)
You can use Array.prototype.filter() for this use case
filterByDate = () => {
const { partners, endDate, startDate } = this.state;
partners.forEach(partner => {
partner.submissions = partner.submissions.filter(sub => moment(partner.submissions[i].addedAt).isBetween(startDate, endDate));
});
};

Array prop returns Observer so can't access at [0]

I passed Array but got Observer here's my code:
In Component1
data() {
return {
myWords: [],
}
}
//...
await axios.post(this.serverUrl + router, {
voca: text,
category: this.buttonGroup.category.text
})
.then(res => {
this.myWords.push({
voca: this.voca,
vocaHeader: this.vocaHeader,
category: res.data.savedVoca.category,
date: res.data.savedVoca.date,
id: res.data.savedVoca._id
})
this.myWords.push({voca:"test"})
})
.catch(err => {
console.log(err)
})
In Component2
props: {
myWordsProp: {
type: Array,
default: () => ([])
},
},
mounted() {
console.log(this.myWordsProp)
console.log(this.myWordsProp[0]) //returns undefined
},
And I expected an Array but I get Observer so I can't get values from this.myWordsProp[0] why?
//this.myWordsProp
[__ob__: Observer]
0: {
category: "ETC"
date: "2018-11-21T15:31:28.648Z"
id: "5bf57a503edf4e0016800cde"
voca: Array(1)
vocaHeader: Array(1)
...
}
1: {__ob__: Observer}
length: 2
__ob__: Observer {value: Array(2), dep: Dep, vmCount: 0}
__proto__: Array
//this.myWordsProp[0]
undefined
I found a clue that when I test it outside of axios it worked as I expected.
Vue wraps data and props into reactive objects. Use vue-devtools plugin in your browser as an alternative to viewing the ugly observer in the console.
In your code, the object behaves correctly. It’s only in the console that it ‘looks’ different.
Anyway, you can also click on the ... to expand the node and get the value from the console.
https://github.com/vuejs/vue-devtools
I found a solution It's because of sending props before get data from server.
This is my whole of postVocas function It returns promise
postVocas: function (voca) {
if (!voca || voca.length < 1) return
let router = "/api/voca"
let text = ""
text += `${this.vocaHeader[0].english}, ${this.vocaHeader[0].korean}\n`
voca.forEach((x, index) => {
text += `${voca[index].english}, ${voca[index].korean}\n`
})
return axios.post(this.serverUrl + router, {
voca: text,
category: this.buttonGroup.category.text
}).then(res => {
this.myWords.push({
voca: this.voca,
vocaHeader: this.vocaHeader,
category: res.data.savedVoca.category,
date: res.data.savedVoca.date,
id: res.data.savedVoca._id
})
}).catch(err => {
console.log(err)
})
},
And await till get data from server.
This one is function where execute My postVocas function.
sendVocaToTable: async function () {
let reformedText = this.reformText(this.text)
this.voca = this.formatTextToVoca(reformedText)
await this.postVocas(this.voca)
this.$router.push({
name: 'Table',
params: {
vocaProp: this.voca,
tableHeaderProp: this.vocaHeader
}
})
},

Categories