Reputation: 1
I am beginner React developer and I've run into this problem while fetching API. The API that I'm using returns an array of objects of different characters. The problem is that some of the parameters of an object have a link as their value, meaning that I can't just render
with a key and it's value. Here is a snippet of an object from this API:
{
"url":"link",
"name":"Alys Karstark",
"gender":"Female",
"culture":"Northmen",
"born":"",
"titles":["Lady of Winterfell"],
"spouse":"link",
"allegiances":["link",
"link"],
"books":["link"],
}
So, I figured I would build a method that upon the arrival of the original list would go through the character objects and go through the keys that have links in them and fetch said links, pulling the name from them for display. After that, I pull a random object of a character from the sorted list and display it.
Here's the fetch upon the mount:
componentDidMount(){
fetch(defaultQuery)
.then(response => response.json())
.then(result => {
this.setState({result: result})
this.optimizeCharacterInfo()
})
.catch(error => error)
}
And here's the function that does the optimisation:
getRandomChar(){
this.setState({randomCharacter: _.sample(this.state.optimizedList)})
}
optimizeCharacterInfo() {
const optimizedList = this.state.result.map(char => {
for (let key in char) {
if (linkKeys.includes(key)) {
if (!Array.isArray(char[key])) char[key] = [char[key]];
const names = [];
char[key].forEach(link => {
fetch(link)
.then(response => response.json())
.then(result => {
names.push(result.name);
char[key] = names;
})
.catch(error => error);
});
}
}
char.isOptimized = true;
return char;
});
this.setState({ optimizedList: optimizedList });
this.getRandomChar();
}
render() {
const { randomCharacter } = this.state;
return (
<div>
<Header headerText={homeText} />
{randomCharacter && randomCharacter.isOptimized ? (
<MainPage randomCharacter={randomCharacter} />
) : null}
</div>
);
}
Though for some reason whenever I load up the page this is what it looks like:
name: Alyn Velaryon
gender: Male
culture: Valyrian
born: In 115 AC or later, at Hull, Driftmark
died: In or between 171 AC and 176 AC, at sea
titles: Lord of the TidesMaster of DriftmarkAdmiralMaster of ships
aliases: Alyn of HullThe OakenfistLord Oakenfist
father:
mother:
spouse: link
allegiances: link
and I am expecting:
name: Alyn Velaryon
gender: Male
culture: Valyrian
born: In 115 AC or later, at Hull, Driftmark
died: In or between 171 AC and 176 AC, at sea
titles: Lord of the TidesMaster of DriftmarkAdmiralMaster of ships
aliases: Alyn of HullThe OakenfistLord Oakenfist
father:
mother:
spouse: name of the spouse
allegiances: allegiance 1, allegiance 2
Meaning that it did not use the filtered list. Once I look up through the dev tools both the list and the random character have all the links transfered into fetched names. So what gives? I'm going to guess that this is solvable by doing a promise, because if I set a timeout on setting the random character it works, but no matter how much i try I cannot get a working promise. How do I get the function to set the isOptimized
to true ONLY when all the links needed are fetched?
Upvotes: 0
Views: 497
Reputation: 196002
setState
is async, so calling the optimizeCharacterInfo
right after it will not do what you want, as the result
is not yet in the state.
Luckily setState
takes a second parameter, which is run after the state is set.
so change
.then(result => {
this.setState({result: result})
this.optimizeCharacterInfo()
})
to
.then(result => {
this.setState({result: result}, this.optimizeCharacterInfo)
})
and the same goes for the optimizeCharacterInfo
so change also
this.setState({ optimizedList: optimizedList })
this.getRandomChar()
to
this.setState({ optimizedList: optimizedList }, this.getRandomChar)
Upvotes: 1