I'm getting files from my input: other = event.target.files; which starts as a global variable and console.logs as FileList {0: File, 1: File, 2: File, 3: File, length: 4}. I'm adding this to my state(this.state.songList, which starts as null) and if(this.state.songList != null) I'm adding the list of songs from this.state.songList to my FormData.
.then(()=> {
this.setState({songsList : other})
}).then(()=> {
if(this.state.songList != null) {
Array.from(this.state.songsList).forEach(song => {
data.append('songs', song);
})
}
console.log(other);
console.log(this.state.songList)
})
For some reason the code inside the if statement almost never gets ran because this.state.songList is always undefined in the console. If I run the code in the second .then without the if statement it gets ran and adds my files to the FormData so I know for a fact that this.state.songList is not empty in my second .then.
Why does the code in the second .then not get ran when the if(this.state.songList != null) is surrounding it although it gets ran without so I know it doesn't equal null and what can I do to fix this issue of state getting read as null.
You're running into this issue because setState is an async function that won't necessarily update by the time your then executes. You should use the callback function for setState to do whatever you need to do with the new value.
this.setState({
songlist: other
}, () => {
// code to execute after the songs update in state
if(this.state.songList != null) {
Array.from(this.state.songsList).forEach(song => {
data.append('songs', song);
})
}
})
Hopefully that helps!
Related
I am doing an axios.get call on an API, in React. When I run it the first time, I get the results that I expect. When I run it the next time and search for the same thing, I get more results than I had originally. Then if I search again, for the same thing, I get even more results that I had the second time. It keeps adding new results to the old ones without starting a brand new search. If I search for a different keyword, then I get results with both the new keyword results and the old ones that I did before.
There is probably an easy fix to this, but how do I get it to discard the old results and create new ones? I have tried creating CancelTokens, but haven't been successful.
I am currently putting the results into a new array and I set that array equal to nothing when I first render the component, so I don't understand how this problem is happening in the first place.
Thanks for any help you can offer!
async componentDidMount() {
//searches the api for the hashtag that the user entered
newArray.length=0;
newArray=[];
await axios.get(`https://laffy.herokuapp.com/search/${this.props.toSearch}`).then(function(response) {
returnedKeywordSearch = response.data;
}) //if the api call returns an error, ignore it
.catch(function(err) {
return null;
});
//goes through the list of locations sent from the api above and finds the latitude/longitude for each
var count = 0;
while (count < returnedKeywordSearch.length) {
locationToSearch = returnedKeywordSearch[count].location;
if (locationToSearch !== undefined) {
var locationList = await axios.get(`https://api.mapbox.com/geocoding/v5/mapbox.places/${locationToSearch}.json?access_token=pk.eyJ1IjoibGF1bmRyeXNuYWlsIiwiYSI6ImNrODlhem95aDAzNGkzZmw5Z2lhcjIxY2UifQ.Aw4J8uxMSY2h4K9qVJp4lg`)
.catch(function(err) {
return null;
});
if (locationList !== null) {
if (Array.isArray(locationList.data.features) && locationList.data.features.length)
{
locationCoordinates.push(locationList.data.features[0].center);
if (returnedKeywordSearch[count].location!== null && returnedKeywordSearch[count].location!==""
&& locationList.data.features[0].center !== undefined)
{newArray.push({
id: returnedKeywordSearch[count].id,
createdAt: returnedKeywordSearch[count].createdAt,
text: returnedKeywordSearch[count].text,
name: returnedKeywordSearch[count].name,
location: returnedKeywordSearch[count].location,
coordinates: locationList.data.features[0].center
});
}
}
}
}
count++;
}
//tweetSpots is set to null in the initial component state set up
this.setState({tweetSpots: newArray});
this.setState({ done: true}); //sets done to true so that loading animation goes away and map displays
}
Okay, I figured out this problem is actually on the server side in Express where the data isn't being reset. So, I'll close this one and open a question about Express.
Yesterday I had a behaviour that I don't understand by using Immer.js and setState together. I was using a setState (in a bad way, by the way) when fetching my data and this fetch was called at each endReached of my SectionList.
This setState looked like this:
this.setState((prev) => {
let sections = prev.sections
/* Extract alive topics from "topics"(array retrieved from fetch)*/
let topicsSection1 = topics.filter((card) => !card.states.is_killed)
/* Extract killed topics from "topics"(array retrieved from fetch)*/
let topicsSection2 = topics.filter((card) => card.states.is_killed)
if (sections[0] && sections[0].data)
sections[0].data = positionExtracted > 1 ? sections[0].data.concat(...topicsSection1) : topicsSection1
if (sections[1] && sections[0].data)
sections[1].data = positionExtracted > 1 ? sections[1].data.concat(...topicsSection2) : topicsSection2
return {
sections: sections,
position: response.position,
lastPage: response.laftPage
}
})
and everything worked just fine.
However, I have a function that is called when you press on the topic, and it changes the "opened" value of the topic in the data array to indicate to the list that "this topic" is open.
This function calls the "produce" function of Immer.js
And this function looks like this:
_onPressTopic = (id_card) => {
this.setState(produce((draft) => {
if (draft.sections[0] && draft.sections[0].data)
draft.sections[0].data = draft.sections[0].data.map((item) => {
if (item.id === id_card)
item.opened = !item.opened
return item
})
if (draft.sections[1] && draft.sections[1].data)
draft.sections[1].data = draft.sections[1].data.map((item) => {
if (item.id === id_card)
item.opened = !item.opened
return item
})
}))
}
MY PROBLEM IS:
If I open a topic and then my list data goes through this function, then when an endReached is called again, either I get an error "This object is not expensive", or my list data is not modified at all. And if instead of my first setState, I use a produce from Immer, everything works again.
What I don't understand is: Why does everything work perfectly if I only use Immer.js or just SetState, but as soon as I try to use both together, they don't seem to get along?
Thank you for your answers,
I hope I made it clear !
Viktor
I am using a promise to dispatch an asynchronous action in react. I just want to change the properties of an object. The function works. If I wrap the code in a promise and after doing Promise().then works but if I do myFunction(params).then() the then is not called here is my function. (It is recursive):
export function setValue(propertyPath, value, obj) {
console.log("setting the value")
return new Promise((resolve, reject) => {
// this is a super simple parsing, you will want to make this more complex to handle correctly any path
// it will split by the dots at first and then simply pass along the array (on next iterations)
let properties = Array.isArray(propertyPath) ? propertyPath : propertyPath.split(".")
// Not yet at the last property so keep digging
if (properties.length > 1) {
// The property doesn't exists OR is not an object (and so we overwrite it) so we create it
if (!obj.hasOwnProperty(properties[0]) || typeof obj[properties[0]] !== "object") obj[properties[0]] = {}
// We iterate.
return setValue(properties.slice(1), value, obj[properties[0]])
// This is the last property - the one where to set the value
} else {
// We set the value to the last property
obj[properties[0]] = value
console.log("modified object")
console.log(obj)
return resolve(obj)
}
}
)
}
export function performPocoChange(propertyPath, value, obj) {
console.log("we are indeed here")
return (dispatch) => {
setValue(propertyPath, value, obj).then((poco) => {
console.log("poco is here")
console.log(poco)
dispatch(changePoco(poco))
})
}
}
the console shows "we are indeed here" but not "Poco is here" and changePoco (that is the action) is not called. However if I do a then right after the brackets of the promises and log it shows the log.
I am not an expert in JS. I am trying really hard and this really got me. Any ideas?
Many thanks in advance
You are not resolveing the promise except in the else so the then doesn't get executed.
superagent.get(URL).then((res) => {
for(let i in res.body) {
if (i==='has_rejected_advisories') {
console.log(i + "="+res.body[i]);
}
}
})
.catch((err) => err.message));
My result is:
has_rejected_advisories=false
But I am not able to use res.body[i] outside this function, i.e I want superagent function to return this value in a boolean variable to use it elsewhere.
ex.
a = superagent.get(URL).then((res) => {
for(let i in res.body) {
if(i==='has_rejected_advisories') {
console.log(i + "="+res.body[i]);
}
}
})
.catch((err) => err.message));
if(a===false){/*do this*/}
This is because the superagent.get(url) call is asynchronous. The value given to a is a Promise
Since this is async, the if (a === false) is actually executing before the function body passed to .then. You will either need to move this logic to the .then function, or use something like async/await if you like the synchronous looking syntax.
On top of jerelmiller's great advice you need to note the following:
Try this:
create a global var assuming it's a string
var mysares = ""
This example will only bring back 1 string back of everything!! Not single element. Also if you can't get the standard Fetch() to work don't try other methods like axios or superagents. Now use our global like so:
superagent.get(URL).then((res) => {
for(let i in res.body) {
if (i==='has_rejected_advisories') {
//Add comments as will help you
//to explain to yourself and others
//what you're trying to do
//That said if this iteration produces
//correct data then you're fine
//push my result as a string
mysares = res.body[i];
//infact what's in row 1?
mysares = res.body[0];
//Actually I code my own JSON!!
mysares = res.body[1];
console.log(i + "="+mysares);
}
}
})
.catch((err) => err.message));
Now you can do whatever:
if(mysares===false){/*do this*/
alert(playDuckHunt());}
Things to note:
res.body[i] is an iteration
You cannot use it outside of the function
Because:
It's local to that function
You don't know what position of 'i' is even if you could use it as you will be outside of your loop
One last thing:
Loops loop through loops or arrays etc.
So (in real world) you can't just request the value of the loop
unless you agree the position of data to be released,
type,and bucket (where it's going to be displayed or not).
Hope this helps!
PS> we need to know where 'has_rejected_advisories' is in the JSON so send us your json url as it must be a column/obj header name. Or it's any old 'a' then var a can be your "false"
In constructor:
this.state = {a:null};
In some function:
superagent.get(URL).then(
(res) => {for(let i in res.body)
{
if(i === 'has_rejected_advisories')
{
this.setState({a:res.body[i]})
}
}
}).catch((err)=>(err.message));
In render:
console.log(this.state.a);
Inside then() the value could be used using state variable but there are many scenarios we could not use them, like if we want to perform all the operations under constructor i.e Initializing state variable, calling superagent and changing the state variable and using the state variable.
I am trying to publish an updated object but not trying to change the state through the following code in react:
addPlaceToSearch = (place) => {
if (place != this.state.search_text) {
var search = $.extend({}, this.state.search);
if (!search.added_places.includes(place)) {
search.added_places.push(place);
PubSub.publish('searchUpdated', search);
}
}
}
Thus, I am using $.extend({}, this.state.search) to clone the object into another variable, modify that particular variable and then publish it. But when I execute the line 5, and I put a breakpoint on line 6, I can see that the this.state.search is also changed with place being pushed in it's added_places key (which I do not want). How is this happening? And how can I prevent it? I have also tried Object.assign({}, this.state.search) instead of $.extend({}, this.state.search).
EDIT
I even tried the most trivial solution, here it is:
addPlaceToSearch = (place) => {
if (place != this.state.search_text) {
if (!this.state.search.added_places.includes(place)) {
var xyz = {};
for (var key in this.state.search) {
xyz[key] = this.state.search[key];
}
xyz.added_places.push(place);
PubSub.publish('searchUpdated', xyz);
}
}
}
Still, the xyz.added_places.push(place) line changes my this.state.search object too! Kindly help me out.
Finally, after two hours of hard work, I figured out the solution by making a deep copy of the original array. I will read about the differences between shallow and deep copy as given in this answer, till then here is the solution:
var xyz = $.extend(true, {}, this.state.search);
xyz.added_places.push(place);
You can do this better without jQuery using Object.assign()
var xyz = Object.assign({}, this.state.search)
xyz.added_places.push(place)