Problem with calling function in componentDidMount() - javascript

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 });
})
}

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();
});
}

setState inside a Promise function in a useEffect with hooks?

I'm trying to set the value of a hook inside a Promise function inside a useEffect() and being able to store the returned promise value in the fruit hook so I can access it in the return function of MyComponent()
This is what I tried so far:
const MyComponent = () => {
const [fruit, setFruit] = useState('')
useEffect(() => {
// my promise function that returns a string (a random fruit)
promiseFunction()
.then(value => setFruit(value))
}, [])
return <div>fruit ? fruit : Loading...</div>
}
I have tried many approachs so far but none of them worked.
useEffect(() => {
promiseFunction()
.then(value => setFruit(() => value))
}, [])
useEffect(() => {
promiseFunction()
.then(value => setFruit((value) => { fruit = value }))
}, [])
After all that, the returned value of the hook is still empty.
There are no errors messages or anything, I tried debugging using another useEffect and it still returns an in the console.
How can I get the returned value of the promise function and store it in the fruit variable?
The first code example looks perfect to me. My only concern is that the value is not being correctly returned from the function itself.
Have you tried to log the return from the promiseFunction? Another way to write this code would be to use a function that you can call inside the useEffect itself, like this:
useEffect(() => {
async function loadFruitData() {
const promiseFunctionReturn = await promiseFunction()
console.log(promiseFunctionReturn)
setFruit(promiseFunctionReturn)
}
//Call the function
loadFruitData();
}, [])
This should give you a better way to see whats happening, and I also find it more readable.
But remember, if you're trying to console.log() the data right after the setFruit, it wouldn't show you the updated state, it happens because React has a queue that make the updates caused by the useState, that make it looks like an asynchronous update.

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

Getting an error after using setState with a promise

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"))}

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)
}

Categories