Waiting for request to finish before performing operation ReactJS - reactjs

I have an axios call inside my react application
}).then(res =>
this.setState({
name: res.data.fname,
sName: res.data.sname,
percentage: res.data.percentage,
result: res.data.result,
showResult: true,
type: this.typeHandler(res.data.percentage)
}));
}
after the request is done AND the state is set, I want to set input forms to blank. So i tried this
}).then(res =>
this.setState({
name: res.data.fname,
sName: res.data.sname,
percentage: res.data.percentage,
result: res.data.result,
showResult: true,
type: this.typeHandler(res.data.percentage)
})
).then(
this.setState(prevState =>{
return {inputName: '', inputSname: ''}
}))
}
using another promise, being resolved after the first, but the forms are still set to blank before the request state is being set. I have tried using a callback as well, but the first .then() method, does not accept a second argument. How can I solve this?

setState can get a call back
https://reactjs.org/docs/react-component.html#setstate
setState(updater[, callback])
instead of another then pass this callback to setState

Related

Cann't set the state data from response data

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.

How can I display a spinner in a React App?

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.

Send Rest call in a loop

I have a Rest API that in order to get the end data AKA:ENDDATA I need to send few requests (depends how deep). So when sending the first request the return response will have in the body the nextUri and so on…
Basically I need to execute a list of Rest calls based on the nextUri till I reach to the end ENDDATA.
Is there a preferred way to do this with React & Redux\redux-thunk.
export const find = () => dispatch => {
dispatch({ type: FIND_CUSTOMER_START });
axios({
method: 'post',
url: '/v1/...',
headers: { 'X': 'XXX1' },
auth: {
username: 'XXX1',
password: 'XXX'
},
data: 'SELECT * FROM .....'
}).then(function (response) {
// Need to find the nextUri ... before update teh state
dispatch({
type: SUCCESS,
payload: response.data
});
}).catch(function (error) {
dispatch({ type: ERROR, payload: error });
});
}
Thank you
Define your api call in a redux action
Inside a component lifecycle method you can call this action. As well as map the state from the store to your component.
When the action updates the state it will cause a re-render and call the lifecycle method. In the lifecycle method you can check to see if ENDDATA is true and if not call the action again.
Inside render you can have the same conditional as in the lifecycle method and have it return a spinner to indicate loading.

Why the first SetState always affected and the second not in react native?

I am using setState inside componentDidMount() to set the state of the same variables but I realise that the first setState is always affected and variables take the values defined on the first setState. I want the second to overwrite the first and give the variables the values of the second setState. Even if I change the order the same result. Why does this happen?
To be precise I am speaking about the latitude and longitude variables.
componentDidMount() {
fetch('myUrl')
.then((response) => response.json())
.then((responseJson) => {
Geocoder.setApiKey('myUrl');
Geocoder.getFromLocation(responseJson.comune_nascita).then(
json => {
var location = json.results[0].geometry.location;
this.setState({
Load:false,
latitude:location.lat,
longitude:location.lng
})
},
error => {
alert(error);
}
);
})
.catch((error) => {
console.log(error)
});
navigator.geolocation.getCurrentPosition(position => {
this.setState({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
error: null,
});
},
error => this.setState({ error: error.message }),
{ enableHighAccurancy: false, timeout: 2000, maximumage: 2000 }
);
}
Your question seems to indicate that the "first setState" is the setState called in the fetch request and the "second setState" is the setState called in the getCurrentPosition method of navigator. According to this I have some answers and suggestions.
This is a problem with your code being asynchronous. As you can see the fetch is called and then once it is complete it will set the state to whatever the latitude and longitude is. The second call is not an asynchronous call, it is called as soon as componentDidMount is called.
Essentially, the second setState call is actually getting called first as soon as the component mounts AND THEN the first setState overwrites it once the fetch is completed.
To fix this I would suggest restructuring your methodology of how this feature will work. The asynchronous call will always finish second and therefore you may want to switch what your default values are.

Set loading state before and after an action in a React class component

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

Resources