Search code examples
reactjsreact-lifecycle

How to fix recursively updating state?


I am bulding an app using newsapi. i am facing two issue on my state. i fetch data using api and assign it to my state. and use it in my view.

Issue no 1

My view gets rendered before my app receives the data.

Issue no 2

When I try to update my state after a new fetch. it recursively updates the set of data again and again.

import React, {Component} from 'react';
import NewsComponent from './NewsComponent/NewsComponent'

class News extends Component {
    state = {
        displayStatus: false,
        newsItems: []
    };

    toogleDisplayHandler = () => {
        if(this.state.displayStatus===true){
        this.setState({displayStatus:false})
        }
        else{
        this.setState({displayStatus:true})
        }
    }

    render(){   
        const NewsAPI = require('newsapi');
        const newsapi = new NewsAPI('d6da863f882e4a1a89c5152bd3692fb6');
        //console.log(this.props.keyword);
        newsapi.v2.topHeadlines({
            sources: 'bbc-news,abc-news',
            q: this.props.keyword
        }).then(response => {
            //console.log(response)
            response.articles.map(article => {
                //console.log(article);
                return(
                    //console.log(this.state.newsItems)
                    this.setState({
                        newsItems: [...this.state.newsItems, article],
                    })
                    //this.state.newsItems.push(article)
                                        
                )
            });
        });
    
        let Article = null;    
            
        Article = (
            <div>
                {
                this.state.newsItems.map((news, index) => {
                    return (
                    <NewsComponent key={index}
                        title={news.title}
                        url={news.url}
                        description={news.description}
                        author={news.author}
                        publish={news.publishedAt}
                        image={news.urlToImage}
                    />
                    )
                })
                }
            </div>
        )

    return (
        <div className="App">
        
            {Article}

            <button onClick={this.toogleDisplayHandler}>
                {this.state.displayStatus === true ? "Hide Article" : "Display Articles"}
            </button>
        </div>
        
    )
    }
    
}

export default News;

Please help me to resolve this issue.


Solution

  • You should never setState in render as that would cause an infinite loop. Do it in componentDidMount or the constructor.

    I would also recommend not using map for simply iterating over a list. Array.map is a function that is useful for returning an array that is constructed by iterating over another array. If you want to run some code for each element of an array use Array.forEach instead.

    Like this:

    import React, { Component } from "react";
    import NewsComponent from "./NewsComponent/NewsComponent";
    
    class News extends Component {
      state = {
        displayStatus: false,
        newsItems: []
      };
    
      toogleDisplayHandler = () => {
        if (this.state.displayStatus === true) {
          this.setState({ displayStatus: false });
        } else {
          this.setState({ displayStatus: true });
        }
      };
    
      componentDidMount = () => {
        const NewsAPI = require("newsapi");
        const newsapi = new NewsAPI("d6da863f882e4a1a89c5152bd3692fb6");
    
        newsapi.v2
          .topHeadlines({
            sources: "bbc-news,abc-news",
            q: this.props.keyword
          })
          .then(response => {
            response.articles.forEach(article => {
              this.setState({
                newsItems: [...this.state.newsItems, article]
              });
            });
          });
      };
    
      render() {
        let Article = null;
    
        Article = (
          <div>
            {this.state.newsItems.map((news, index) => {
              return (
                <NewsComponent
                  key={index}
                  title={news.title}
                  url={news.url}
                  description={news.description}
                  author={news.author}
                  publish={news.publishedAt}
                  image={news.urlToImage}
                />
              );
            })}
          </div>
        );
    
        return (
          <div className="App">
            {Article}
    
            <button onClick={this.toogleDisplayHandler}>
              {this.state.displayStatus === true
                ? "Hide Article"
                : "Display Articles"}
            </button>
          </div>
        );
      }
    }
    
    export default News;