Can't set state with axios get request? - reactjs

So I want to set my state for my bookings to be my result.data, the request is working and I can console.log(result.data). However when I try to set the state and then check that state, it's empty.
What is going on? Does it have something to do with my params I'm passing to axios?
My initial state looks like: this.state = {bookingArrayByDate: []}
Tried setting the state outside "then" but that didn't work either
componentDidMount() {
let today = moment(new Date())
let dateToSend = today.format('YYYY-MM-DD')
axios
.get(
`http://localhost:8888/booking-backend/fetch-reservation.php/`,
{ params: { res_date: dateToSend } }
)
.then((result: any) => {
console.log(result.data)
this.setState({
bookingArrayByDate: result.data
})
})
console.log('this is outside', this.state.bookingArrayByDate)
}
The array at the bottom will be empty when i console log it.

Your code is fine. What you are experiencing is the standard asynchronous behavior ofthis.setState(). When you console.log() your state, there is no guarantee that it was updated in the time between your setState() and the console.log() execution.
In fact, its almost 100% unlikely that it was changed within that time, because React actually batches setState() requests by putting them in a queue until the entire event is completed. In your case, the setState() wouldn't actually occur until after the componentDidMount() logic finishes.
What you can do is pass a call-back as the 2nd argument to this.setState(), that call-back is executed directly after the state has finished updating, thus ensuring you are printing the updated state.
componentDidMount() {
let today = moment(new Date())
let dateToSend = today.format('YYYY-MM-DD')
axios
.get(
`http://localhost:8888/booking-backend/fetch-reservation.php/`,
{ params: { res_date: dateToSend } }
)
.then((result: any) => {
this.setState({
bookingArrayByDate: result.data
}, () => console.log(this.state.bookingArrayByDate)) <-- you can swap this whatever function you need.
})
}

Related

Setting state with setState doesn't change values

I am calling fetchProducts in a loop (array.map) from a child component, but don't want have to call it when it's already been done before. Just for testing, I have added "i" which I update after the first call, but this.state.i === 0 is always true, so I keep fetching the same data.
fetchProducts = async (id) => {
if(this.state.i === 0) {
let url = "http://localhost/mysite/wp-json/mysite/product/" + id
await fetch(url)
.then(response => response.json())
.then(result => {
this.setState({
...this.state,
products: this.state.products.concat([result]),
i: 1
})
})
}
}
Am I missing something here? Or is there a better way to cache it? The idea is to check for a product ID in this.state.products, instead of "i".
I have two observations.
Firstly, you are setting the state once a promise is resolved.
Secondly, you are calling the fetchProducts through map, which is synchronous.
So, what's happening is, your fetchProducts is keeps on getting called synchronously. That means it doesn't wait for your previous promise to resolve. But you are setting your state after the promise resolved. Hence, it is not guaranteed that when your consecutive fetchProducts are called you would have set the state in the previous function calls.

How to handle async data in ComponentDidUpdate?

I'm new to React and got some questions now.
I use componentDidUpdate to fetch data when the button is clicked. I manage the results data with redux. It's seem like componentDidUpdate is async function, because "console.log" always execute first before the fetch function is done. So I always got [] in the console the first time I clicked the button.
Suppose I have 2 actions I want to do when I clicked the button. First fetching the data, after that use that data and pass to another actions. How can I wait until the fetch data done after that the console.log is execute? Please help me.
state = {
searchClick: false,
keyword: "pizza",
};
componentDidUpdate(prevProps, prevState) {
if (
this.state.searchClick &&
prevState.searchClick !== this.state.searchClick
) {
// send get request to fetch data
this.props.searchResults(this.state.keyword);
//Update searchClick state
this.setState({ ...this.state, searchClick: false });
// console log the result data
console.log(this.props.results);
}
}
The console log always executes first because searchResults is async. What you need to do is returning a promise inside searchResults:
const searchResults = (keywords) => {
// fetch with the keywords and return the promise
// or any promise that resolves after your search is completed
return fetch(...);
};
componentDidUpdate(prevProps, prevState) {
if (
this.state.searchClick &&
prevState.searchClick !== this.state.searchClick
) {
// send get request to fetch data
this.props.searchResults(this.state.keyword).then(() => {
//Update searchClick state
this.setState({ ...this.state, searchClick: false });
// console log the result data
console.log(this.props.results);
});
}
}

setState is not updating in my other method

I'm very new to react and i'm confused why my state is not updated in another method of mine see example below.
fetchMovies = () => {
const self = this;
axios.get("https://api.themoviedb.org/3/trending/movie/day?api_key=XXXXXXX")
.then(function(response){
console.log(response.data)
self.setState({
collection: response.data.results
})
console.log(self.state.collection)
});
}
makeRow = () => {
console.log(this.state.collection.length);
if(this.state.collection.length !== 0) {
var movieRows = [];
this.state.collection.forEach(function (i) {
movieRows.push(<p>{i.id}</p>);
});
this.setState({
movieRow: movieRows
})
}
}
componentDidMount() {
this.fetchMovies();
this.makeRow();
}
When inside of fetchMovies function i can access collection and it has all the data but this is the part i can't understand in the makeRow function when i console log the state i would of expected the updated state to show here but it doesn't i'm even executing the functions in sequence.
Thanks in advance.
the collection is set after the async call is resolved. Even though makeRow method is called after fetchMoview, coz of async call, u will never know when the call will be resolved and collection state will be set.
There is no need to keep movieRows in the state as that is just needed for rendering. Keeping html mockup in the state is never a good idea.
So u should just call fetchMoviews in the componentDidMount and render the data in as follows:
render() {
const { collection } = this.state;
return (
<>
{
collection.map(c => <p>{c.id}</p>)
}
</>
)
}
make sure the initial value for collection in the state is [] .
The setState() documentation contains the following paragraph:
Think of setState() as a request rather than an immediate command
to update the component. For better perceived performance, React may
delay it, and then update several components in a single pass. React
does not guarantee that the state changes are applied immediately.
To access the modified state you need to use the function signature setState(updater, [callback]), so in your case it should be;
self.setState({
collection: response.data.results
}, () => { // Will be executed after state update
console.log(self.state.collection)
// Call your make row function here and remove it from componentDidMount if that is all it does.
self.makeRow()
} )

how to remove lag in setState using callback function in react

how to remove lag in setState using callback function in react
tried using callback but still lag state and data in array state cannot be mapped
mapfn(){
ServerAddr.get(`/dishes/read/meal/category`)
.then(res => {
const getmeal6 = res['data']['data'];
this.setState({ getmeal6 },()=>{
console.log('log233',this.state.getmeal6);
});
});
console.log('log232',this.state.getmeal6);
this.state.getmeal6.map((item) => {
return (
this.setState({
maparr:[...this.state.maparr,item.id],
})
);
});
console.log(this.state.maparr,'val32');
}```
in log233 the state is proper but in log232 the state lags with 1
The problem with your current code is that both http calls, and calls to setState are asynchronous.
// this call is asynchronous
ServerAddr.get(`/dishes/read/meal/category`)
.then(res => {
const getmeal6 = res['data']['data'];
// this is also asynchronous
this.setState({ getmeal6 },()=>{
});
});
// this call happens synchronously! It will almost certainly happen before the two
// async calls complete
this.state.getmeal6.map((item) => {
return (
this.setState({
maparr:[...this.state.maparr,item.id],
})
);
});
If you want to do something after your http call and the setState are both resolved, you need to either be inside the then function of a promise, or in the callback function of setState.
So something like this:
// this call is asynchronous
ServerAddr.get(`/dishes/read/meal/category`)
.then(res => {
const getmeal6 = res['data']['data'];
// this is also asynchronous
this.setState({ getmeal6 },()=>{
// this is where you need to put the
// code you want to happen after the http call and setState
});
});
That said, you need to reconsider what you are trying to do - either by refactoring your state management using something like Redux, or by using async await in your method, to make your code a little easier to read, or by a totally new approach to the problem at hand.

the state is not updating after setting new

I am new to react and facing a problem. I am fetching data from an API using Axios then I have to set that data into state and pass that value in another component as props.
My problem is i am changing state using this.setState after fetching API , but the state is not changing. So I am sharing my code below.
constructor(props){
super(props);
this.state={
employeeData:[] // setting empty value
}
}
ComponentDidMount(){
console.log("Current State"+JSON.stringify(this.state)) ///output = []
axios.get("http://localhost:8080/hris/api/employee/get/all")
/// getting values , can see them in network
.then(response => response.data)
.then((data) => {
this.setState({ employeeData: data }) ///setting new value
console.log(this.state.employeeData) /// can see fetched data
})
.catch(err=> console.log(err))
console.log("2Nd STATE "+this.state) /// again empty state, there is no fetched data
}
Then I have to pass that state in another component.
render(){
return(
<div className=" col-md-12" style={viewData}>
<div >
<p><b>All Employee Details</b></p>
</div>
<Table data={this.state.employeeData}/>
</div>
)
}
setState is async function which takes some time to set your new state values. So printing new state after this line will give you previous state only and not new state.
You need a callback, to check the changed state,
this.setState({ employeeData: data }, () => console.log("2Nd STATE "+this.state))
Another thing is, axios is meant to reduce number of .then(). With axios you will get direct JSON value. You can remove 1 .then().
axios.get("http://localhost:8080/hris/api/employee/get/all") /// getting values , can see them in network
.then(response => {
this.setState({ employeeData: response.data }, () => console.log("2Nd STATE "+this.state)) // This will give you new state value. Also make sure your data is in `response.data` it might be just `response`.
console.log(this.state.employeeData) // This will give you previous state only
})
.catch(err=> console.log(err))
Your console.log("2Nd STATE "+this.state) is returning empty because it probably runs before that axios request completes.
Initially your render method gets called with empty state which is probably throwing an error. You need to handle the render with loading state until your request completes.
For example your render could look like this,
render() {
return (!this.state.employeeData.length) ?
(<div>Loading..</div>) :
(
<div className=" col-md-12" style={viewData}>
<div >
<p><b>All Employee Details</b></p>
</div>
<Table data={this.state.employeeData} />
</div>
)
}
setState is async so you cannot see the change instantly where setState() is called. in order to view the change, you need to do a callback.
this.setState({ employeeData: data },()=>console.log(this.state.employeeData)) ///setting new value
change the code to above format and you can see the change in state once it is changed
I guess this is where you are going wrong give it a try with this. It has got nothing to do with react. The way you used Axios is wrong.
ComponentDidMount(){
console.log("Current State" + JSON.stringify(this.state));
axios
.get("http://localhost:8080/hris/api/employee/get/all")
.then(response => {
this.setState({ employeeData: response.data });
})
.catch(err => console.log(err));
};
Alright, both of these problems are occurring because Axios and this.setState() are asynchronous. Its hard to explain asynchronous programming in a single StackOverflow answer, so I would recommend checking this link: [https://flaviocopes.com/javascript-callbacks/][1]
But for now to get your code to work, switch it to this
ComponentDidMount() {
console.log(this.state); // Obviously empty state at beginning
axios.get("http://localhost:8080/hris/api/employee/get/all")
.then(res => res.data)
.then(data => {
this.setState({employeeData: data}, () => { // Notice the additional function
console.log(this.state); // You'll see your changes to state
})
})
.catch(err => console.log(err));
console.log(this.state); // This won't work because Axios is asynchronous, so the state won't change until the callback from axios is fired
}
The part that most new React developers don't tend to realise is that this.setState() like axios is asynchronous, meaning the state doesn't change immediately, the task of actually doing that gets passed on as a background process. If you want to work with your state after it has changed, the this.setState() function provides a second parameter for doing just that
setState(stateChange[, callback])
taken from the react docs. Here the second parameter is a callback (a.k.a function) you can pass that will only get triggered after the state change occurs
// Assuming state = {name: "nothing"}
this.setState({name: "something"}, () => {
console.log(this.state.name); // logs "something"
});
console.log(this.state.name); //logs "nothing"
Hope this helps!!.

Resources