Reputation: 319
Hi I am fetching data from an api and I would like to take the data and render it to the dom but I am the error "Uncaught TypeError: Cannot read property 'map' of undefined at Topicselect.render"
Here is essentially what I am doing, although I have abstracted away anything that is not directly relevant to the question, such as actual topic names, imports, etc :
class Topics extends Component{
constructor(props){
super(props);
this.state = {
topics: []
}
}
componentWillMount(){
fetch('/api').then((res)=>r.json().then((data)=>{
// push topics into this.state.topics somehow
})
console.log(this.state.topics) //returns ['topic1','topic2','topic3'];
}
render(){
const list = this.state.topics.map((topic)=>{
return(<li>{topic}</li>);
})
return(
<ul>
{list}
</ul>
)
}
}
Can anyone tell me how to fix this? I saw an answer on here that said to use componentDidMount instead of componentWillMount but that isn't working for me
Upvotes: 3
Views: 9164
Reputation: 2796
Make sure you use setState()
to update your state, otherwise render()
won't be triggered to update the dom. Also make sure you don't just overwrite the current state but add your new topics to the old ones. (not relevant for this case, but still important to mention)
One way to do it would be:
componentDidMount() {
var currentTopics = this.state.topics;
fetch('/api').then((res) => r.json().then((data) => {
currentTopics.push(data);
}));
this.setState({'topics': currentTopics});
}
But you can also call setState()
inside the loop. setState()
does not work synchronously so it will first wait if there are some other changes to be made before it will actually execute the changes and then trigger render
.
componentDidMount() {
fetch('/api').then((res) => r.json().then((data) => {
this.setState((state) => ({ topics: [...state.topics, data]}));
}));
}
Upvotes: 1
Reputation: 3586
You are missing a closing bracket )
after the fetch and it's indeed recommended to use componentDidMount()
instead of componentWillMount()
for fetching data from an API.
Also don't forget to use this.setState({ topics: data.howeverYourDataIsStructured });
after you receive the data from the API to ensure a rerender of the component.
class Topics extends Component{
constructor(props){
super(props);
this.state = {
topics: []
}
}
componentDidMount() {
fetch('/api').then((res)=>r.json().then((data)=>{
this.setState({ topics: data.topics });
}));
console.log(this.state.topics) //returns [];
}
render() {
console.log(this.state.topics) //returns [] the first render, returns ['topic1','topic2','topic3'] on the second render;
return(
<ul>
{this.state.topics.map(topic => (
<li>{topic}</li>
))}
</ul>
)
}
}
Upvotes: 2