moesh
moesh

Reputation: 319

Fetch data and then render it to dom React

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

Answers (2)

Philipp
Philipp

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

Tom Van Rompaey
Tom Van Rompaey

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

Related Questions