0

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.

Community
  • 1
  • 1
user27019
  • 29
  • 7
  • 1
    fetch data using api , do this in componentDidMount() do setState there. don't fetch api data inside render() – Jayavel Feb 08 '19 at 07:59

2 Answers2

4

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;

ManavM
  • 2,918
  • 2
  • 20
  • 33
  • Thanks for the support. i tried your suggestion it almost worked.one minor problem though. in my previous code once i change the keyword from another component, using this.props.keyword it fetch new results. after moving that section in to componentDidMount that stopped working – user27019 Feb 08 '19 at 08:54
  • I wont be able to help you without knowing what the code is. Try debugging the issue...if you find yourself stuck, ask on SO. We're happy to help :) – ManavM Feb 08 '19 at 09:25
  • i get input from anothr Js file called TopBar. once that feild get blured that pass the key word to app and app pass it to news. the above code is from news.js. all three component code is in this jsfiddle link https://jsfiddle.net/7mv6o92a/ – user27019 Feb 08 '19 at 09:52
  • well the problem is that your componentDidMount is called only once (when the component mounts). you want to call your API whenever your 'keyword' prop changes...for that you need to specify this behaviour in the [componentDidUpdate](https://reactjs.org/docs/react-component.html#componentdidupdate) lifecycle method. – ManavM Feb 08 '19 at 10:34
  • in that function you can check if `prevProps.keyword` is different from `this.props.keyword` and if it is, you can send the network request to fetch the new data – ManavM Feb 08 '19 at 10:36
1

1) You can add a check either your state has the data which you want to show on screen to render the view.

2) Please use ComponentDidMount React life cycle function to fetch data from an external source and update this data in the state. In the Render method, it will keep calling it recursively.

Mubeen Khan
  • 997
  • 1
  • 10
  • 11