简体   繁体   中英

The state updates, but doesn't show up on the page

here is the function i am using to update my news website project with infinite scrolling:

import React,{ Component } from "react";
import NewsItem from './NewsItem';
import Spinner from "./Spinner";
import PropTypes from "prop-types";
import InfiniteScroll from "react-infinite-scroll-component";

export class News extends Component {
    static defaultProps={
        country: "in",
        pageSize: 6,
        category: 'general',
        totalResults: 0
    }
    static propTypes={
        country: PropTypes.string,
        pageSize: PropTypes.number,
        category: PropTypes.string
    }
    constructor(props) {
        super(props);
        this.state={
            articles: [],
            loading: true,
            page: 1
        }
    }
    async updateNews() {
        const url=`https://newsapi.org/v2/top-headlines?country=${this.props.country}&category=${this.props.category}&apiKey=[apikey]&page=${this.props.page}&pageSize=${this.props.pageSize}`;
        this.setState({ loading: true })
        let data=await fetch(url);
        let parsedData=await data.json()
        this.setState({
            articles: parsedData.articles,
            totalResults: parsedData.totalResults,
            loading: false
        })
    }
    async componentDidMount() {
        this.updateNews()
    }
    // prevClick=async () => {
    //     this.setState({ page: this.state.page-1 })
    // }
    // nextClick=async () => {
    //     this.setState({ page: this.state.page+1 })
    //     this.updateNews();
    // }
    fetchMoreData=async () => {
        this.setState({ page: this.state.page+1 })
        console.log(this.state.page)
        const url=`https://newsapi.org/v2/top-headlines?country=${this.props.country}&category=${this.props.category}&apiKey=[api key]&page=${this.props.page}&pageSize=${this.props.pageSize}`;
        let data=await fetch(url);
        let parsedData=await data.json()
        this.setState({
            articles: this.state.articles.concat(parsedData.articles),
            totalResults: parsedData.totalResults
        })
    }
    render() {
        return (
            <div className="container my-4">
                <h2 className="my-5 text-center">Top Headlines of today!</h2>
                <InfiniteScroll
                    dataLength={this.state.articles.length}
                    next={this.fetchMoreData}
                    hasMore={this.state.articles.length!==this.state.totalResults}
                    loader={<Spinner />}>
                    <div>
                        <div className="row">
                            {this.state.articles.map((element) => {
                                return <div className="col-md-4" key={element.title}>
                                    <NewsItem title={element.title} description={element.description} imageUrl={element.urlToImage} newsUrl={element.url} author={element.author} date={element.publishedAt} />
                                </div>
                            })}
                        </div>
                    </div>
                </InfiniteScroll>
            </div>
        )
    }
}

export default News

the issue is that change in state of page shows up in the console but when the page is updated, the same data/news gets repeated. I tried using 'this.state.page' instead of 'this.props.page' but there is no difference i guess. Is there something i am missing?

Yes the problem is with your fetchMoreData function. The way state update work is that it does not update instantly instated its batch all the state update and update it into the next cycle.

You can run this code to better understand this example.

import react, { useState, useEffect } from "react";
 
const App = () => {
      const [state, setstate] = useState(1);
    
      const handleState = () => {
        console.log("before", state);
        setstate(state + 1);
        console.log("after", state);
      };
      return (
        <div>
          <button onClick={handleState}>Check</button>
        </div>
      );
    };
    
    export default App;    

The solution of your problem is that take out your page size from state add 1 then after success call increment it

fetchMoreData = async () => {
    let page= this.state.page + 1; // increment like this
    const url = `https://newsapi.org/v2/top-headlines?country=${this.props.country}&category=${this.props.category}&apiKey=[api key]&page=${page}&pageSize=${this.props.pageSize}`;
    let data = await fetch(url);
    let parsedData = await data.json();

   this.setState({
      articles: this.state.articles.concat(parsedData.articles),
      totalResults: parsedData.totalResults,
      page: page
    });
  };

Here is the problem.

You shouldn't use the same object or array when updating the state. If so, the component doesn't know state as updated.

So for line 8: You should copy your array with spread operator ... and concat that array.

the problem here was that the increment in the page wasn't happening with set State, so the trick was to replace 'this.props.page' from the api url with a different variable to store the page no. and then incrementing it (see the code below to for this to make sense) so that the page gets incremented later but we do get the correct data. I discovered this delay it later on when i was turning the class based components to function based(with hooks).(the delay might still be there so just make sure to cover the delay, by adding 1 in the url too, ie, 'pageNo+1' instead of 'pageNo' although this won't be necessary if you are using function based components with hooks)

The change is quite a lot but if i change it to function based, then the page counter is initialized outside the function and then just increment it inside as the mathematical incrementing is faster then using the setState or anything else. here is the proper working code:

import React,{ useEffect,useState } from "react";
import NewsItem from './NewsItem';
import Spinner from './Spinner'
import PropTypes from "prop-types";
import InfiniteScroll from "react-infinite-scroll-component";

const News=(props) => {
    const [articles,setarticles]=useState([])
    const [loading,setloading]=useState(true)
    const [page,setpage]=useState(1)
    const [totalResults,settotalResults]=useState(0)
    const updateNews=async () => {
        const url=`https://newsapi.org/v2/top-headlines?country=${props.country}&category=${props.category}&apiKey=${props.apiKey}&page=${pageNo}&pageSize=${props.pageSize}`;
        let data=await fetch(url);
        let parsedData=await data.json()
        setarticles(parsedData.articles)
        settotalResults(parsedData.totalResults)
        setloading(false)

    }
    useEffect(() => {
        updateNews()
    },[])
    let pageNo=0;
    const fetchMoreData=async () => {
        pageNo=page+1
        const url=`https://newsapi.org/v2/top-headlines?country=${props.country}&category=${props.category}&apiKey=${props.apiKey}&page=${pageNo}&pageSize=${props.pageSize}`;
        let data=await fetch(url);
        let parsedData=await data.json()
        setarticles(articles.concat(parsedData.articles))
        settotalResults(parsedData.totalResults)
        setpage(pageNo)
    }
    return (
        <>
            <h1 className="text-center" style={{ margin: '35px 0px',marginTop: '90px' }}>Top Headlines of today!</h1>
            <InfiniteScroll
                dataLength={articles.length}
                next={fetchMoreData}
                hasMore={articles.length!==totalResults}
                loader={<Spinner />}>
                <div className="container">

                    <div className="row">
                        {articles.map((element) => {
                            return <div className="col-md-4" key={element.title}>
                                <NewsItem title={element.title} description={element.description} imageUrl={element.urlToImage} newsUrl={element.url} author={element.author} date={element.publishedAt} />
                            </div>
                        })}
                    </div>
                </div>
            </InfiniteScroll>
        </>
    )
}
News.defaultProps={
    country: "in",
    pageSize: 6,
    category: 'general',
    totalResults: 0
}
News.propTypes={
    country: PropTypes.string,
    pageSize: PropTypes.number,
    category: PropTypes.string
}
export default News

thank you everyone who helped me in this, as i was able to solve the problem partially but not completely now its done after a good nap.

have a nice day!!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM