Getting an error after using setState with a promise - javascript

Code:
onDragEnd = {
(event) => this.setState({ playerMarkerPositionFuture: event.nativeEvent.coordinate })
.then(() => alert("hello"))
}
When the following code gets executed I receive this error:
undefined is not an object evaluating
('_this.setState({playerMarkerPositionFuture: event.nativeEvent.coordinate}).then')
If I remove the promise everything works as expected so it means that most likely I used the promise in a wrong way:
onDragEnd={
(event) => this.setState({ playerMarkerPositionFuture: event.nativeEvent.coordinate })
}

setState accepts a callback, but it doesn't return a promise. See docs
Calls to setState are asynchronous - don’t rely on this.state to reflect the new value immediately after calling setState. Pass an updater function instead of an object if you need to compute values based on the current state (see below for details).
(event) => {
this.setState({playerMarkerPositionFuture: event.nativeEvent.coordinate }, () => alert("hello"));
}

setState doesn't return a Promise. It is most probably a void function.

Instead of using a promise to call alert after setState is finished, use a callback instead
onDragEnd={(event) =>
this.setState({
playerMarkerPositionFuture: event.nativeEvent.coordinate
}, () => {
alert("hello")
})}

onDragEnd={(event) => this.setState({ playerMarkerPositionFuture: event.nativeEvent.coordinate }).then(() => alert("hello"))}
This is a wrong practise of using setState() method. You can only use a callback using setState().
Right Practise:
onDragEnd={(event) => this.setState({ playerMarkerPositionFuture: event.nativeEvent.coordinate },()=>{
alert("hello")
})
Have a look at this article:
https://medium.com/#voonminghann/when-to-use-callback-function-of-setstate-in-react-37fff67e5a6c

setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied.
https://reactjs.org/docs/react-component.html#setstate
so you could use setState(updater, callback) to execute code after the state was altered, to be on the save side

If you want use promise ...another solution could be
setStateAsync(state) {
return new Promise((resolve) => {
this.setState(state, resolve)
});
}
and use that ...
this.setStateAsync(
{playerMarkerPositionFuture: event.nativeEvent.coordinate}
)
.then(() => alert("hello"))

Try this
onDragEnd={(event) => this.setState({ playerMarkerPositionFuture: event.nativeEvent.coordinate },() => alert("hello"))}

Related

new Promise with set state receives an arrow function

I was going through a react code base where I see the Promise function calling setstate with 2 arguments, I cannot exactly decipher how exactly this is working, everywhere I have seen a setState being created having one object argumemnt, but here we have a resolve argument that gets a setstate with an object and an arrow function which is calling resolve. The other arrow function, which I don't have any idea what it's doing and how this code is working
this is what I have for state
constructor(props){
super(props);
this.state = {
articles:[],
loading:true,
page:1,
totalResults : 0
}
}
This is the code that I have in my codebase which I have trouble understanding.
handleNextClick = async ()=>{
await new Promise(resolve => this.setState({page: this.state.page+1}, () => resolve())); // same as await this.setState({page: this.state.page+1});
await this.updateNews();
}
As you can see Promise is receiving theresolve arrow function which is calling setstate with an object and an arrow function. Can someone please explain how this is working exactly.
The code doesn't make a lot sense which is probably why it's difficult to understand.
setState has a callback option. So you don't need a promise, and you don't need to await the setState either. Further, you probably don't need to await updateNews either so you can remove the async from the handleNextClick function.
This is likely what the function should look like.
handleNextClick () {
// Set the new state, then use the callback option
// to call `updateNews`
this.setState( { page: this.state.page + 1 }, () => {
this.updateNews();
});
}

State of array is not updated in ComponentDidMount

I have a weird situation, where I have an array as a state:
this.state = { ActivityItem: []} and I am pushing values to it from library that calls an API like this:
getDataFromKit(dateFrom) {
Kit.getSamples(stepCountSample, (err, results) => { //api call
if (err) {
return;
}
const newData = results.map(item => { return { ...item, name: 'itemAmount' } });
this.setState({ d: [...this.state.ActivityItem, ...newData] })
})
Then, I call this method from ComponentDidMount() for array to be loaded
componentDidMount() {
this.getDataFromHealthKit(ONEDAYINTERVAL);
console.log("values are", this.state.ActivityItem)
}
Now, the weirdest part: somehow the array is empty in ComponentDidMount, but when I display elements of Array in return of render() function it displays all the values that were added correctly. How is that possible and how I might fix that?
setState is asynchronous in nature. Therefore, logging the state just immediately after setting it can give this behaviour but if set properly, it will display the required content which is happening in your case. Also componentDidMount is called only once in the beginning so you can check for logs in componentDidUpdate method.
State updates are async in nature. If you want to print the state soon after setting your state in class component, then pass a function to the 2nd argument of setState.
Like this
componentDidMount() {
this.getDataFromHealthKit(ONEDAYINTERVAL);
// remove console.log
}
...
getDataFromKit(dateFrom) {
...
this.setState({ ActivityItem: [...this.state.ActivityItem, ...newData] }), () => {
console.log("values are", this.state.ActivityItem) //<----
}
})
...
}
use prevstate while updating the state value. React setState is an asynchronous update and does batch updating. Using prevState makes sure that the state value is updated before calculating new state value.
getDataFromKit(dateFrom) {
let stepCountSample = {
startDate: dateFrom.toISOString(),
type: "Type1"
};
Kit.getSamples(stepCountSample, (err, results) => {
//api call
if (err) {
return;
}
const newData = results.map(item => {
return { ...item, name: "itemAmount" };
});
this.setState(prevState => {
ActivityItem: [...prevState.ActivityItem, ...newData];
});
});
}
DOCUMENTATION would help understand the concept
Also, console.log would directly not give the updated state, since state updates are batched. You can use a callback method to setState function. The callback will run only after successfull updation of state value

Why is a function call without an argument in setState called before setState?

As was pointed out to me in another question, if I have a function call without an argument in setState, randomQuoteIndex(), and the function uses a state set in that setState, it's called before setState.
componentDidMount() {
fetch('https://gist.githubusercontent.com/nataliecardot/0ca0878d2f0c4210e2ed87a5f6947ec7/raw/1802a693d02ea086817e46a42413c0df4c077e3b/quotes.json')
.then(response => response.json())
.then(quotes => this.setState({
quotes,
randomQuoteIndex: this.randomQuoteIndex(),
isDoneFetching: true
}));
}
randomQuoteIndex() {
return random(0, this.state.quotes.length - 1);
}
This results in an error because the state of quotes isn't available at the time of randomQuoteIndex() call.
However, if I change randomQuoteIndex() to instead use a passed in quotes parameter, as below, it works.
componentDidMount() {
fetch('https://gist.githubusercontent.com/nataliecardot/0ca0878d2f0c4210e2ed87a5f6947ec7/raw/1802a693d02ea086817e46a42413c0df4c077e3b/quotes.json')
.then(response => response.json())
.then(quotes => this.setState({
quotes,
randomQuoteIndex: this.randomQuoteIndex(quotes),
isDoneFetching: true
}));
}
randomQuoteIndex(quotes) {
return random(0, quotes.length - 1);
}
This is not what I expected; I had assumed that the state of quotes would be available at the time randomQuoteIndex() is called since it was called within setState. Why is randomQuoteIndex() called before setState even though it's inside it?
It is because setState is an async operation. You can still use your original function by using setState as a callback
componentDidMount() {
fetch('https://gist.githubusercontent.com/nataliecardot/0ca0878d2f0c4210e2ed87a5f6947ec7/raw/1802a693d02ea086817e46a42413c0df4c077e3b/quotes.json')
.then(response => response.json())
.then(quotes => this.setState({
quotes,
isDoneFetching: true
}, () => {
this.setState({
randomQuoteIndex: this.randomQuoteIndex(),
}); //this callback will be executed after your state is updated.
}));
}
randomQuoteIndex() {
return random(0, this.state.quotes.length - 1);
}
So, essentially once a setState function is performed, the updated values will only reflect after the update lifecycle is finished, which means you can get the values either in componentDidUpdate method or by using the setState function as a callback.
I wouldn't recommend this solution but in this case, the function randomQuoteIndex() will get the updated state values without having any parameter passed.
Thats because at the time you passed the object to setState, you are executing inline call to this.randomQuoteIndex.
state.quotes did not exist yet in the state.
constructor() {
this.state = { quotes: [] };
}
componentDidMount() {
fetch('https://gist.githubusercontent.com/nataliecardot/0ca0878d2f0c4210e2ed87a5f6947ec7/raw/1802a693d02ea086817e46a42413c0df4c077e3b/quotes.json')
.then(response => response.json())
.then(quotes => {
this.setState({
quotes,
randomQuoteIndex: this.randomQuoteIndex(),
isDoneFetching: true
})
});
}
randomQuoteIndex() {
return random(0, this.state.quotes.length - 1);
}

Am i promise chaining correctly?

onSubmit(e) {
e.preventDefault();
const user = {
fname: this.state.firstname,
lname: this.state.lastname,
email: this.state.email,
username: this.state.username,
password: this.state.password
}
new Promise((resolve,reject) => {
this.props.fetchUser(this.state.username)
.then(res => {
this.setState({failed: this.props.exists})
if(!this.state.failed)
this.props.registerUser(user)
})
.then(res => {
this.setState({registered: this.props.status});
resolve();
})
})
}
This is my attempt at chaining promises. The idea was that registered should update correctly to the state of this.props.status (true/false).
When this.props.registerUser is called in the first promise, it changes this.props.status to true. However, registered is being set to false (which is the value of this.props.status before registerUser is called), rather than true.
I know for sure that this.props.status is changing to true, but the state of registered isn't changing.
I'm new to this stuff.
I'm assuming that fetchUser and registerUser are functions that return promises. In that case, you do not need to wrap the call for fetchUser in a new Promise(...) since it will return a promise when invoked.
The reason that the second then(...) is not being called, is that you never return a promise from the first then(...).
if(!this.state.failed)
this.props.registerUser(user)
should become
if(!this.state.failed)
return this.props.registerUser(user)
With these two modifications, your code should look like so
this.props.fetchUser(this.state.username)
.then(res => {
this.setState({
failed: this.props.exists
});
if (!this.state.failed) {
return this.props.registerUser(user)
}
})
.then(res => {
this.setState({
registered: this.props.status
});
})
Furthermore, you would expect to read the result of fetchUser(...) on the res object rather than the component props.
One final caveat that you should be aware of is that setting the state and reading it immediately after, is not guaranteed to always work as expected. The safe way to do it, is to pass a function as your second argument to setState and that will be invoked when the state is updated by React.
The simplest way to do it in this case is to avoid reading the state altogether and instead using a temporary variable.
const exists = this.props.exists;
this.setState({
failed: exists
});
if (!exists ) {
return this.props.registerUser(user)
}

Problem with calling function in componentDidMount()

I am quite new in React.js and I have trouble with assigning my state through a static function call from another class. This code:
componentDidMount() {
this.setState(() => ({ movies: MovieService.getMovies() }));
}
Does not set my state. I believe the problem lies in my usage of the asynchronous function since if I use console.log(MovieService.getMovies()), It shows Promise {<pending>}. However, I am still confused on how to fix this, thank you for the help!
componentDidMount() {
MovieService.getMovies().then(moviesList => {
this.setState({ movies: moviesList }));
})
}
Assuming getMovies returns a list of movies, you need to wait for the promise it returns to fulfil and then assign the value to your state
Your code will be setting the movies field of your state to a Promise object representing the pending promise returned from the call to MovieService.getMovies()
Try making the following adjustment to your code to resolve the issue, by first resolving the promise and then assigning the result to your components state:
componentDidMount() {
MovieService.getMovies().then((movies) => {
this.setState({ movies: movies });
})
}

Categories