Couldn't setState from the then function of the Promise - javascript

I'm trying to update the state from the promise which I received using the fetch function.
componentDidMount(){
fetch(url).then((responseText) => {
var response = responseText.json();
response.then(function(response){
this.setState(response);
});
});
}
I was getting the error that the setState is not an function
Then, I tried to bind(this) to pass the this value like below.
componentDidMount(){
fetch(url).then((responseText) => {
var response = responseText.json();
response.then(function(response){
this.setState(response);
});
}).bind(this);
}
It is not working now also. Same error again.

This is because of the scoping of this, so you're on to something when you're trying to use Function.prototype.bind. Your mistake is that you don't bind all the way down to the last anonymous function. What you probably want to do is use arrow functions all the way, like this:
componentDidMount(){
fetch(url)
.then((responseText) => responseText.json())
.then((response) => this.setState(response));
}
Arrow functions always keep the context of this.

Sorry, Just now found that I didn't bind the this variable properly.
Now, It is fixed.
componentDidMount(){
fetch(url).then((responseText) => {
const response = responseText.json();
response.then(function(response){
this.setState(response);
});
}.bind(this));
}

Your second promise doesn't have the current this context. You can use an arrow function here as well.
componentDidMount(){
fetch(url).then((responseText) => {
return responseText.json();
})
.then((response) => {
this.setState(response);
});
}
Also, chaining instead of nesting your promises will help with the legibility and might help you to avoid callback hell.

You also have the wrong method to setState it should look something like setState({name : 'string'})

Related

React/Jest How to wait until useEffect has run?

I have the following code:
const doSomething = useCallback(someFunction);
useEffect(() => {
// doSomething takes in the data and invokes the callback at the end as newData
doSomething(data, (newData: string) => {
setData(newData);
});
}, [data, doSomething]);
When I want to test this, it only works when I use setTimeout with 1 or more ms. I suppose this is because the function in useEffect has not run yet and thus has not updated the component, correct me if I'm wrong. How can I work around this without setTimeout? Or do I need a completely different approach? Help is appreciated.
maybe try a wrapper function, for example:
async function handleSomething(){
const res = await doSomthing()
setData(res)
}
then call this function inside useEffect.

Transform class method from callback flow to promise

I have the following sample code,
class GetResultsFromDatabase{
results : SiteTable[];
db : AccessDatabase;
constructor(){
this.db = new AccessDatabase();
}
getAllLevelsSites(){
this.db.getSitesDb(this.fullFillSites);
}
private fullFillSites(data : SiteTable[]){
this.results= data;
this.db.getUrlsDb(this.fullFillUrls);
}
private fullFillUrls(data : UrlsTable[]){
data.map( (current) => this.results[this.results.findIndex(obj => obj.id =
current.token)].urlTable = current );
}
}
If some code outside class calls the method "getAllLevelsSite" i want to return the complete results array with all the values fullfilled (after the last "fullFillUrls function complete).
The "db" object uses the mysql library so all the methods works only with callbacks.Which options do i have ? Is it possible to create any kind of Promise in getAllLevelsSite method in a way that the code that is calling it can use async await syntax ? Or the only way is to pass one callback function to the method getAllLevelsSite ?
Thanks in advance.
Best regards.
Your example does not provide impl. for getSitesDb so will guess how the callback looks like...
getAllLevelsSites() {
return new Promise((resolve, reject) => {
this.db.getSitesDb(this.fullFillSites, (arr, err) => {
if (err) {
reject(err);
} else {
resolve(arr);
}
});
});
}
Now wherever you are consuming this method you can use await
Untested (so I could be wrong), but rewriting getAllLevelsSites method to something like the below might work:
getAllLevelsSites(): Promise<SiteTable[]> {
return new Promise((resolve) => {
this.db.getSitesDb(resolve);
});
}
Based on the signature of fullFillSites, I've assumed that the callback passed to this.db.getSitesDb will be invoked with at least one argument (of type SiteTable[]).
Since getAllLevelsSites returns in a promise (in this code above), you should be able to then use async/await syntax.
Unrelated: You use Array.prototype.map to iterate over data in fullFillUrls, but don't do anything with the array that map returns. Seems like you should swap map out for forEach.

return cleanup function from within finally block in react

Within a React function element I have a use Effect hood that has some callback based stuff going on. At the end of the callback chain, I need to return a function to cleanup on dismounting. How is that done.
I have something like this:
ReactFunctionElement=(..)=>{
useEffect(()=>{
asyncCall()
.then(..)
.cath(..)
.finally(
return ()=>{cleanup stuff}
)
},[some filters])
}
But the cleanup stuff never gets run. I don't know how, or if it is even possible, to lift that back out into the useEffect and return.
I think you need something like this:
useEffect(() => {
// Closure variable
const thingToCleanup = null;
asyncCall()
.then(thingToCleanup = thingFromAsync...)
.catch(...);
return () => {
// cleanup stuff using thingToCleanup
});
} ,[some filters]) }
Your useEffect function has to return the clean up function, .finally returns a Promise which won't work but you also don't return that (JavaScript will implicitly return undefined). But you need access to some variables from the setup code in the cleanup so you store those in a closure to use later during clean up.
you can subscribe an api call and in cleanup function you can unsubscribe it. here is an example:
useEffect(() => {
api.subscribe(userId);
return () => api.unsubscribe(userId);
}, [ userId ])

Problem with Scope Functions in Method from Vue.js

I have a SetInterval inside to a Promise in Axios. When I try to execute a function in this SetInterval, I have the follow error:
methods: {
getJson() {
axios.post(url, FormObject, config)
.then(response => {
var searchId = JSON.stringify(response.data.searchId)
this.sendStatus(searchId)
var status = setInterval(function(){ this.sendStatus(searchId) },
30000);
})
.catch(err => (this.error = err))
},
sendStatus(searchId){},
}
The first call (this.sendStatus(searchId)) working correctly. However, the setInterval return this error:
Uncaught TypeError: this.sendStatus is not a function at eval
You are changing the context of this in your second call, as you are introducing a new function.
If you are using ES6, the easiest way to overcome this, is to use an arrow function instead of the function keyword.
var status = setInterval(() => { this.sendStatus(searchId) },
30000);
})
If you cannot use ES6, you have to use the .bind() function, which is explained in this question. Easier, but dirtier would be to reassign this to a local variable.
var that = this;
Then use that.sendStatus in your callback function.
You need to use an arrow function in your setInterval, like this :
setInterval(() => this.sendStatus(searchId))
Here is a resource explaining more the arrow functions and this

how to call vue router from inside custom function

I use VueCLI and i do have such code inside methods:
submitCheck: function () {
function authUser() {
// returns a promise
}
function uploadFile() {
// also returns a promise
}
// ...
if ( error !== null ) {
EventBus.$emit('showError', error)
} else {
authUser()
.then(
function () {
return uploadFile();
})
.then(
function (data) {
EventBus.$emit('loaderStop')
this.$router.push('/awaiting');
})
.catch(function(error) {
EventBus.$emit('loaderStop')
console.log(error)
})
}
What i want to achieve is to route to /awaiting if all promises are resolved, but since i use this inside an anonymous function it doesnt have router. I am sure many coders met such a problem and needed to route from inside a function. How to do it?
Kalreg.
Multiple ways to handle this, I'd suggest you use arrow functions and learn about their differences to the other function style.
to be clear:
replace
function (data) {
EventBus.$emit('loaderStop')
this.$router.push('/awaiting');
}
with
data => {
EventBus.$emit('loaderStop');
this.$router.push('/awaiting');
}
You question context is not clear enough. If the code is executed in exponent methods, you can use arrow funciton like (agrs,...) => {...}. Otherwise, if this is not the case, you can use bind function like (function() {}).bind(this). Or just import $router in your code module.
Hope this can help you.
The answers from Sandro and Xhua are perfect. I just want to explain, WHY you get the error:
The problem is "this.". It refers to the parent object. So in your case "this." refers to the authUser Object and not to Vue. For your understanding: You could define "var that = this" outside of your authUser object and then use "that." inside. Or you go for the more sophisticated solutions.

Categories