how to update a state variable immediately - reactjs

How should I code so that I can save the API response to the state immediately and access the updated response outside the setState().
state={
response: []
}
this.setState({
response: res // res is coming from API response
});
console.log(this.state.response); // it should show the API response instead of empty []

Use callbacks:
...
this.setState({
response: res // res is coming from API response
}, function() {
console.log(this.state.response);
});
...

Make the method calling setState to be async, and let res await answer. Something like
yourMethod = async (params) => {
const res = await //api call
this.setState({
response: res
)}
}

componentDidMount is the recommended lifecycle hook to fetch initial state data
class App extends React.Component {
state = {
data: [],
}
async componentDidMount() {
const res = await fetch(url);
const data = await res.json();
this.setState({ data }, console.log('state updated', this.state));
}
render() {
return (
/* render data */
);
}
}

Related

How to wait for the promise to resolve and update the state

This is what my code looks like:
constructor(props) {
super(props);
this.state = {
docs: []
};
}
async componentDidMount() {
await this.quizes();
console.log(this.state.docs);
}
quizes = () => {
firebase
.firestore()
.collection("quiz")
.get()
.then(result => {
const docs = result.docs.map(doc => {
return { uid: doc.id, ...doc.data() };
});
this.setState({ docs });
});
};
Currently console.log(this.state) returns empty docs when I am trying to update it with documents from firestore.
setState is asynchronous. If you are sure that your collection is not empty then you can see your state using:
this.setState({ docs }, () => console.log(this.state);
The function as second argument of setState is run only when the asynchronous task of setting the state is done, thus you are going to see the updated state.
In order to await your quizes function it also needs to be async and use the await syntax rather than promises.
For example this code should achieve the desired outcome:
constructor(props) {
super(props);
this.state = {
docs: []
};
}
async componentDidMount() {
await this.quizes();
}
quizes = async () => {
let result = await firebase.firestore().collection("quiz").get()
const docs = result.docs.map(doc => {
return { uid: doc.id, ...doc.data() }
});
return this.setState({ docs }, () => {
console.log(this.state.docs);
});
};
EDIT:
setState uses a callback. In order to guarantee the state has been set at the time of logging, use callback within the quizes function.

How to fix, does not wait for a respons, but begin to render

areasGet = async(Index) => {
let res = await fetch(`https://api.hh.ru/areas/113`)
let json = await res.json()
this.setState({
areaData: json,
isLoadArea: true
})
// console.log(json)
}
specGet = async(Index) => {
let res = await fetch(`https://api.hh.ru/specializations`)
let json = await res.json()
this.setState({
specData: json,
isLoadSpec: true
})
}
industriesGet = async(Index) => {
let res = await fetch(`https://api.hh.ru/industries`)
let json = await res.json()
this.setState({
industriesData: json,
isLoadInd: true
})
}
componentDidMount() {
this.areasGet()
this.specGet()
this.industriesGet()
}
When performing, then one is not filled, then another, randomly in general. Like synchrony, but why the error..
The render () itself works only if all 3 isLoad are true
You need to wait to all of them, and mark to the render that it can render, so, you can introduce a state property, let's call it, isLoaded.
It's value should start with false, and become true only when the 3 ajax requests are done.
You have 2 choices to do so:
async-await syntax
Promise.all
Example:
class MyComp extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoaded: false,
};
}
async componentDidMount() {
await Promise.all([this.areasGet(), this.specGet(), this.industriesGet()]);
this.setState({ isLoaded: true });
}
render() {
const { isLoaded } = this.state;
if (isLoaded) {
return <div>Here is you component that needs all the 3 ajax requests</div>;
} else {
return <div>Loading...</div>;
}
}
}

promise chaining and async, await

I have been stuck on this for a while.
In React component I am trying to fetch data from 3 sources and in addition on fetch url depends on another fetch result. Then I'd like to use Promise.all function to setState just one for all three fetch functions.
I have tried to use async/await combination in every possible way but still promises are pending. What am I missing?
class DuelDetail extends React.Component {
state = {
duel: [],
dataset: [],
algorithms: []
};
fetchDuel = async () => {
var dataset = null
const duelID = this.props.match.params.duelID;
let duel = await axios.get(`http://127.0.0.1:8000/api/duel/${duelID}`,
{'headers': {'Authorization': `Token ${localStorage.getItem('token')}`}})
.then(async (res) => {
dataset = await this.fetchDataset(res.data.dataset)
return [duel, dataset]
}
)
};
fetchDataset = async (datasetId) => {
let res = await axios.get(`http://127.0.0.1:8000/api/dataset/${datasetId}`,
{'headers': {'Authorization': `Token ${localStorage.getItem('token')}`}})
return res
};
fetchAlgorithms = () => {
return axios.get(`http://127.0.0.1:8000/api/algorithm/`,
{'headers': {'Authorization': `Token ${localStorage.getItem('token')}`}})
};
componentDidMount() {
const promises = [this.fetchDuel()[0], this.fetchDuel()[1], this.fetchAlgorithms()]
Promise.all(promises).then(([duelsResponse, datasetsResponse, algorithmsResponse]) => {
this.setState({
duels: duelsResponse.data,
dataset: datasetsResponse.data,
algorithms: algorithmsResponse.data
});
});
}

React native not waiting for response from API before continuing

I have just started playing about with react native and I have a problem that functions aren't waiting for responses before continuing.
So in Chrome my console log displays:
userStore
this state contents
returned data from api / userstore [object Object]
Basically getUserDetails is executed and in that time while the api is being called the setData function runs, and it completes before the api result has been returned.
I would like the getUserDetails functio to complete before setData is called.
I have had a look at resources online, but am at a loss. The code I am using is below (This has been stripped down for ease of reading nb. I am using mobx)
UserScreen.js
constructor (props) {
super(props);
this.state = {
data: null
};
}
async componentDidMount() {
this.props.commonStore.setLoading(true);
await this.props.userStore.getUserDetails('1');
this.setData();
this.props.commonStore.setLoading(false);
}
setData() {
this.setState({
userDetails: this.props.userStore.userDetails
});
console.log('userStore' + this.props.userStore.userDetails)
console.log('this state contents '+ this.state.userDetails);
}
render () {
if(this.props.commonStore.isLoading===false) {
return (<View><Text>Ready!!</Text></View>)
}else{}
return (<View><Text>Loading</Text></View>)
}
}
UserStore.js
#action getUserDetails = (userID) => {
axios.get('http://192.168.1.9/user/' + userID)
.then(response => {
console.log('returned data from api / userstore ' +response.data.user);
this.userdetails = response.data.user;
}).catch(error => {
console.log(error);
this.error = error
}) }
Thanks
If you have stumbled upon the beauty of Mobx, you need to move towards a stateless solution i.e.:
UserScreen.js
componentDidMount() {
this.getUserDetails();
}
async getUserDetails(){
await this.props.UserStore.getUserDetails('1');
}
render () {
const { isLoading, userDetails, error } = this.props.UserStore
return (<View>
{(!!isLoading)?<Text>{userDetails}</Text>:<Text>Loading</Text>}
</View>)
}
UserStore.js
#observable userdetails = {};
#observable isLoading = false;
#observable error = {};
async getUserDetails(userID) {
this.isLoading = true;
try {
await axios.get('http://192.168.1.9/user/' + userID)
.then(response => {
console.log('returned data from api / userstore '+response.data.user);
this.userdetails = response.data.user;
this.isLoading = false;
})
.catch(error => {
console.log(error);
this.error = error
})
} catch (e) {
console.log('ERROR', e);
this.isLoading = false;
}
}
As you are passing the data into an observable array i.e. #observable userdetails = {}; Mobx will automatically update the state, once the promise / await is complete.
P.S. Mobx rules OK!! :o)

AsyncStorage.getItem in react native not working as expected

I am trying to fetch data using AsyncStorage. whenever i call my action creator requestData and do console on the data which is passed , i get something like below .I have two version of getItem .In both the version i get useless value for property field . Property value should be readable
{"fromDate":"20160601","toDate":"20160701","property":{"_40":0,"_65":0,"_55":null,"_72":null},"url":"/abc/abc/xyz"}
async getItem(item) {
let response = await AsyncStorage.getItem(item);
let responseJson = await JSON.stringify(response);
return responseJson;
}
async getItem(item) {
try {
const value = AsyncStorage.getItem(item).then((value) => { console.log("inside componentWillMount method call and value is "+value);
this.setState({'assetIdList': value});
}).then(res => {
return res;
});
console.log("----------------------------value--------------------------------------"+value);
return value;
} catch (error) {
// Handle errors here
console.log("error is "+error);
}
}
componentWillMount() {
requestData({
fromDate: '20160601',
toDate: '20160701',
assetId: this.getItem(cmn.settings.property),
url: '/abc/abc/xyz'
});
}
You are getting property as a promise, you need to resolve it.
Try to use something link that.
assetId: this.getItem(cmn.settings.property).then((res) => res)
.catch((error) => null);
Since AsyncStorage is asynchronous in nature you'll have to wait for it to return the object AND THEN call your requestData method; something like the following -
class MyComponent extends React.Component {
componentWillMount() {
this.retrieveFromStorageAndRequestData();
}
async getItem(item) {
let response = await AsyncStorage.getItem(item);
// don't need await here since JSON.stringify is synchronous
let responseJson = JSON.stringify(response);
return responseJson;
}
async retrieveFromStorageAndRequestData = () => {
let assetId = await getItem(cmn.settings.property);
requestData({
fromDate: '20160601',
toDate: '20160701',
assetId,
url: '/abc/abc/xyz'
}) ;
}
// rest of the component
render() {
// render logic
}
}

Resources