I'm doing in the axios to get data from webAPI . The problem is state is empty
componentDidMount() {
uploadFilesService.getFiles().then((response) => {
this.setState({
fileInfos: response.data,
});
console.log('check 1', response.data)
console.log('check 2', this.state.fileInfos)
});
}
The other project with the same method is OK .
You can the use second argument of setState
this.setState({
fileInfos: response.data,
}, () => {
// do something with the new state
});
From React doc:
The second parameter to setState() is an optional callback function
that will be executed once setState is completed and the component is
re-rendered. Generally we recommend using componentDidUpdate() for
such logic instead.
Related
I am building an app using React and for my homepage, I set state in the componentDidMount lifecycle:
export default class HomePage extends Component {
state = {
posts: [],
token: '',
};
//Display posts when homepage renders
componentDidMount() {
//If token exists, run lifecycle event
if (this.props.location.state.token) {
this.setState({ token: this.props.location.state.token });
}
Axios.get('http://localhost:3000/api/posts/all')
.then((req) => {
this.setState({ posts: req.data });
})
.catch((err) => {
console.log(err.message);
throw err;
});
console.log(this.state);
}
However when I run the console log at the end of the lifecycle method, it shows posts and token as still being empty. I know they are being populated because the posts from the req.data show up in my JSX. Why does it show state being empty when I console log inside the method?
React setState is asynchronous!
React does not guarantee that the state changes are applied immediately.
setState() does not always immediately update the component.
Think of setState() as a request rather than an immediate command to update the component.
this.setState((previousState, currentProps) => {
return { ...previousState, foo: currentProps.bar };
});
I have a React App and I want to show a spinner when processing will take some time to complete, for example an API request, or even lengthy setting of state. I have tried a callback such as:
this.setState({
showSpinner: true,
}, () =>
this.APIgiveMeTheWorld().then(
this.setState({
showSpinner: false,
})
));
and async function with:
await this.setState({
showSpinner: true,
});
this.APIgiveMeTheWorld().then(
await this.setState({
showSpinner: false,
})
);
But at no discernible point does state show showSpinner as being true (therefore my spinner never appears). What is the best way to implement this kind of functionality.
What you can do is to show the Spinner as long the showSpinner state is false and hide it when It became false
{ !showSpinner? <SpinnerComponent /> : <ResultShower /> }
And in your component you can load your data from an API in the componentWillMount lifeCycle and when the loading completed and you have data back from the API's Call you can update the showSpinner state to false, which will automatically hide the SpinnerComponent and show whatever else you want to show.
componentWillMount() {
loadData().then((data) => {
// Here I consider get data from from you API request
this.setState({
data,
showSpinner: false
});
})
}
I see you have passed result of setState in call back then. Did you try:
this.setState({
showSpinner: true,
}, () =>
//should pass a call back function to call right after api return.
this.APIgiveMeTheWorld().then(r => {
this.setState({
showSpinner: false,
})
}));
The problem is with my API function resolving instantaneously.
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!!.
I've created the component, which passes the function to change its state to the child.
//parent component
setSubject = (id) => {
this.setState({
currentSubject: id
});
}
<Subjects authToken = {this.state.authToken} subjects = {this.state.subjects} setSubject = {this.setSubject} />
//child component
<li onClick={() => this.props.setSubject(subject.id)}>Egzamino programa</li>
That state is passed to another component.
<Sections authToken = {this.state.authToken} subject = {this.state.currentSubject} />
From there I am using componentDidUpdate() method to handle this change:
componentDidUpdate() {
if (this.props.subject) {
axios.get(`http://localhost:3000/api/subjects/${this.props.subject}/sections?access_token=${this.props.authToken}`)
.then(response => {
this.setState({
sections: response.data
})
}).catch(err => {
console.log(err)
})
}
}
Everything works as expected, BUT when I try to console.log something in Sections component after I've set currentSubject through Subjects component, that console.log executes endless number of times (so is get request, i guess...) It is not goot, is it? And I cannot understand why this happens..
The bug is in your componentDidUpdate method.
You are updating the state with
this.setState({
sections: response.data
})
When you do that, the componentDidUpdate life-cycle method will be called and there you have the endless loop.
You could make a quick fix by using a lock to avoid this issue. But there might be a better design to solve your issue.
The quick fix example:
if (this.props.subject && !this.state.sectionsRequested) {
this.setState({
sectionsRequested: true,
});
axios.get(`http://localhost:3000/api/subjects/${this.props.subject}/sections?access_token=${this.props.authToken}`)
.then(response => {
this.setState({
sections: response.data,
});
})
.catch(err => {
console.log(err);
});
}
It might better to use componentWillReceiveProps for your case.
You are interested in getting data based on your this.props.subject value. I can see that because you're using it as part of your url query.
You might be interested in using componentWillReceiveProps and componentDidMount instead of componentDidUpdate
componentDidMount(){
if(this.props.subject){
/* code from componentDidUpdate */
}
}
componentWillReceiveProps(nextProps){
if(nextProps.subject && this.props.subject !== nextProps.subject){
/* code from componentDidUpdate */
}
}
I have function which dispatched an action. I would like to display a loader before and after the action. I know that react composing the object passed to setState. the question is how can I update the property in async way:
handleChange(input) {
this.setState({ load: true })
this.props.actions.getItemsFromThirtParty(input)
this.setState({ load: false })
}
Basically, it all worked great if I put this property as part of the application state (using Redux), but I really prefer to bring this property to the component-state only.
you can wrap the setState in a Promise and use async/await as below
setStateAsync(state) {
return new Promise((resolve) => {
this.setState(state, resolve)
});
}
async handleChange(input) {
await this.setStateAsync({ load: true });
this.props.actions.getItemsFromThirtParty(input);
await this.setStateAsync({ load: false })
}
Source: ASYNC AWAIT With REACT
Wrap the rest of your code in the callback of the first setState:
handleChange(input) {
this.setState({
load: true
}, () => {
this.props.actions.getItemsFromThirtParty(input)
this.setState({ load: false })
})
}
With this, your load is guaranteed to be set to true before getItemsFromThirtParty is called and the load is set back to false.
This assumes your getItemsFromThirtParty function is synchronous. If it isn't, turn it into a promise and then call the final setState within a chained then() method:
handleChange(input) {
this.setState({
load: true
}, () => {
this.props.actions.getItemsFromThirtParty(input)
.then(() => {
this.setState({ load: false })
})
})
}
Here's a typescript implementation of an "async-await" setState:
async function setStateAsync<P, S, K extends keyof S>(
component: Component<P, S>,
state:
((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) |
Pick<S, K> |
S |
null
) {
return new Promise(resolve => component.setState(state, resolve));
}
The previous answers don't work for Hooks. In this case you get the following error when passing a second argument to setState
Warning: State updates from the useState() and useReducer() Hooks don't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect().
As the error message says, you need to use useEffect instead in this case (see also this discussion for more detailed information)
Here's what you can do...
Change your action to take in a onFetchComplete callback, along with the input.
Change your handleChange to -
handleChange(input) {
this.setState({ load: true }, ()=>
this.props.actions.getItemsFromThirtParty(input,
()=>this.setState({ load: false }))
);
}
This will ensure the action processor code can invoke back your state change callback even if it's not written in a promise based fashion.
A small update- using promises for the action creators and async/await works great, and it makes the code even cleaner, compared to the "then" chaining:
(async () => {
try {
await this.props.actions.async1(this.state.data1);
await this.props.actions.async2(this.state.data2)
this.setState({ load: false );
} catch (e) {
this.setState({load: false, notify: "error"});
}
})();
Of course it is a matter of taste.
EDIT : Added missing bracket
I know this is about class components... But in functional components I do this to synchronously set the state:
const handleUpdateCountry(newCountry) {
setLoad(() => true);
setCompanyLocation(() => newCountry);
setLoad(() => false);
}
Just worked for my automatic country detection that should set the form to dirty.
One approach not mentioned in any of the other answer is wrapping the operation in a setTimeout. This will also work if you use hooks.
Eg.:
handleChange(input) {
this.setState({ load: true })
setTimeout(() => {
this.props.actions.getItemsFromThirtParty(input).finally(() => {
this.setState({ load: false })
});
});
}