Reputation: 25
I am trying to generate a list of selector options based on external JSON data. The code here is mostly good, except that part of it is being called before the data is loading resulting in no children being appended. I am sure there is a way to implement this but I'm not sure what that way is for my particular situation.
Here is the code:
class PokedexSelector extends Component {
constructor(props) {
super(props);
this.state = {value: "National", pokedexes: []};
this.generatePokedexList();
this.handleChange = this.handleChange.bind(this);
this.generatePokedexList = this.generatePokedexList.bind(this);
this.pokedexList = this.pokedexList.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
generatePokedexList() {
const pokedexes = [];
fetch("https://pokeapi.co/api/v2/pokedex/")
.then(response => response.json())
.then(myJson => {
let results = myJson["results"];
results.forEach(function(pokedex) {
let pokedexName = pokedex["name"];
let pokedexLink = "https://pokeapi.co/api/v2/pokedex/" + pokedexName;
let pokedexDisplayName = capitalize(pokedexName.replace('-',' '));
pokedexes.push(
{
name: pokedexName,
displayName: pokedexDisplayName,
link: pokedexLink
}
);
});
this.state.pokedexes = pokedexes;
console.log(this.state.pokedexes)
})
}
pokedexList() {
if (this.state.pokedexes.length > 0) {
console.log("listing")
return (
this.state.pokedexes.map(pokedex => (
<option>{pokedex.displayName}</option>
))
)
}
}
render() {
return (
<select id="pokedex-selector" value={this.state.value} onChange={this.handleChange}>
{this.pokedexList()}
</select>
)
}
}
export default PokedexSelector;
I tried using componentDidMount()
as below, but I'm not sure how to specifically target one component for changes in this case (the <select>
element).
componentDidMount() {
{this.pokedexList()}
}
Any ideas? Thanks!
Upvotes: 0
Views: 108
Reputation: 6289
You should make your fetch calls before the render
method is triggered, ideally in componentDidMount
and store the response in the state. The component will re-render only when the state
or props
changes.
state
should be updated via this.setState()
method and should not be directly mutated using this.state
.
In your case, since you're trying to mutate the state directly using this.state
the component will not re-render. You should replace it with this.setState()
.
Try this code
class PokedexSelector extends Component {
constructor(props) {
super(props);
this.state = {value: "National", pokedexes: []};
this.handleChange = this.handleChange.bind(this);
this.generatePokedexList = this.generatePokedexList.bind(this);
this.pokedexList = this.pokedexList.bind(this);
}
componentDidMount() {
this.generatePokedexList();
}
handleChange(event) {
this.setState({value: event.target.value});
}
generatePokedexList() {
const pokedexes = [];
fetch("https://pokeapi.co/api/v2/pokedex/")
.then(response => response.json())
.then(myJson => {
let results = myJson["results"];
results.forEach(function(pokedex) {
let pokedexName = pokedex["name"];
let pokedexLink = "https://pokeapi.co/api/v2/pokedex/" + pokedexName;
let pokedexDisplayName = capitalize(pokedexName.replace('-',' '));
pokedexes.push(
{
name: pokedexName,
displayName: pokedexDisplayName,
link: pokedexLink
}
);
});
this.setState({pokedexes: pokedexes}); // use setState()
})
}
pokedexList() {
if (this.state.pokedexes.length > 0) {
console.log("listing")
return (
this.state.pokedexes.map(pokedex => (
<option>{pokedex.displayName}</option>
))
)
}
}
render() {
return (
<select id="pokedex-selector" value={this.state.value} onChange={this.handleChange}>
{this.pokedexList()}
</select>
)
}
}
export default PokedexSelector;
Upvotes: 2
Reputation: 6256
It should be this.setState and not this.state.pokedexes = pokedexes
. Do not mutate state directly.
this.setState({
pokedexes
})
Upvotes: 0