Having multiple API calls in one componentDidMount - possible? - reactjs

I've got a rather tricky problem. Within my componentDidMount method, I have:
1.) State being set on the variable 'dog'
2.) An API call being made via axios, the response of which sets the state of another variable, 'dogName'
This is creating problems (the data I want rendered to the browser isn't rendering) - so is there a better way to write my code?
setData = async () => {
const x = await fetch("https://dog.ceo/api/breed/hound/images");
const y = await x.json();
const z = await y.message;
let newArr = [];
for (let i = 0; i < z.length; i++) {
if (i <= 9) {
newArr.push(z[i]);
}
}
return newArr;
};
componentDidMount() {
this.setState({
loading:true
})
this.setData()
.then(res =>{
this.setState({
loading:false,
dog: res,
})
})
axios.get('http://localhost:3000/dogs')
.then(res => {
this.setState({
dogName:res.data
})
})
.catch(error => {
console.log(error)
})
}

this should do the trick
You listen on both promise resolves at the same time and then execute the setState with all data you got.
My tip for you is: you should look into react hooks or even react-redux to get data not directly in your component code :)
componentDidMount() {
this.setState({
loading: true,
});
const dogPromise = this.setData();
const dogNamePromise = axios.get('http://localhost:3000/dogs');
Promise.all([
dogPromise,
dogNamePromise
])
.then(([dogResponse, dogNameResponse]) => {
this.setState({
loading: false,
dog: dogResponse,
dogName: dogNameResponse.data,
});
})
.catch(error => {
console.log(error);
});
}

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.

Assigning return values from api to state in React

Hi I am trying to call an api assign the returned values to a state object in React, the API is returning values but the values are not being set to state, not understanding what's the reason thank you
handleDDLCommunityChange = event => {
let filesFromApi = []; // ["file1", "file2", "file3", "file4"];
fetch('https://localhost:44352/api/files/Community-1')
.then((response) => {
return response.json();
})
.then(data => {
filesFromApi = data.map(file => { return { value: file, display: file } });
}).catch(error => {
console.log(error);
debugger;
});
console.log(filesFromApi);
this.setState({
files: filesFromApi.map(file => {
return {
fileName: file,
checked: false
};
})
});
};
fetch is an async method. An async method dispatches an action with the callbacks and unblocks following code branch from executing. The callbacks are then used to act on completion (success or failure) of the async method execution.
As you are calling the setState outside of the callbacks of the fetch call's chain, it's not guaranteed to run after the fetch call is done. As Sudheer has pointed out in their comment, you should try to set the state in a then block of the fetch chain.
warning: untested code
handleDDLCommunityChange = event => {
let filesFromApi = []; // ["file1", "file2", "file3", "file4"];
fetch('https://localhost:44352/api/files/Community-1')
.then(response => response.json())
.then(data => {
filesFromApi = data.map(file => ({ value: file, display: file });
this.setState({
files: filesFromApi.map(file => ({
fileName: file,
checked: false
})
})
});
}).catch(error => {
console.log(error);
debugger;
});
};

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

ReactJS setState when all nested Axios calls are finished

I have a problem with updating my state from nested axios call inside forEach loop:
constructor(props) {
super(props);
this.state = {
isLoaded: false,
items: []
};
//Binding fetch function to component's this
this.fetchFiles = this.fetchFiles.bind(this);
}
componentDidMount() {
this.fetchFiles();
}
fetchFiles() {
axios.get('/list')
.then((response) => {
var items = response.data.entries;
items.forEach((item, index) => {
axios.get('/download'+ item.path_lower)
.then((response) => {
item.link = response.data;
})
.catch(error => {
console.log(error);
})
});
this.setState(prevState => ({
isLoaded: true,
items: items
}));
console.log(this.state.items);
})
.catch((error) => {
console.log(error);
})
}
The idea is to get all items from Dropbox using it's API (JavaScript SDK)
and then for each item I also need to call different API endpoint to get a temporary download link and assign it as a new property. Only after all items will get their links attached I want to setState and render the component. Could somebody please help with this, I spend already multiple hours fighting with promises :S
You could use Promise.all to wait for multiple promises. Also keep in mind that setState is async and you wont see immediate changes. You need to pass a callback.
fetchFiles() {
axios.get('/list')
.then((response) => {
var items = response.data.entries;
// wait for all nested calls to finish
return Promise.all(items.map((item, index) => {
return axios.get('/download'+ item.path_lower)
.then((response) => {
item.link = response.data;
return item
});
}));
})
.then(items => this.setState(prevState => ({
isLoaded: true,
items: items
}), () => console.log(this.state.items)))
.catch((error) => {
console.log(error);
})
}
Try making the fetchfiles() function as an asynchronous method by adding the async keyword.Now, we have to wait till the items to get their download link, so add a await keyword before that line which makes the code to wait till the axios call gets completed.
async function fetchFiles() {
axios.get('/list')
.then(async function(response){
var items = response.data.entries;
await items.forEach((item, index) => {
axios.get('/download'+ item.path_lower)
.then((response) => {
item.link = response.data;
})
.catch(error => {
console.log(error);
})
});
this.setState(prevState => ({
isLoaded: true,
items: items
}));
console.log(this.state.items);
})
.catch((error) => {
console.log(error);
})
}
I haven't tested the code, but it should probably work.

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