skip to Main Content

I am working on a React project where I would like to display thousands of articles. I can call them 100 at a time and want to display them as I bring them in. How do I do that?

My current attempt is to fire off useEffect and call the api and then use setState. But whenever I do that it re-renders the component and we get the infinite death loop going.

 const [articles, setArticles] = useState([]);

 useEffect(() => {
    const getArticles = async () => {
      let nextArticleNum = 0;
      let getMoreArticles = true;

      while (getMoreArticles) {
        const articlesRes = await axios.post("link-to-articles", { nextArticleNum: nextArticleNum });
        setArticles([...articles, ...articlesRes.data.articles])
        if (articles.length === 0) {
          getMoreArticles = false;
        } else {
          nextArticleNum = nextArticleNum + 100;
        }
      }
    }

    getArticles();
  }, [])

3

Answers


  1. I tried your code but cant see any probs.

    But in this case i suggest you move nextArticleNum into an useState and setNewArticleNum after get api till articles result length = 0.

    Then, we call the api everytime newArticleNum change.

    const [articles, setArticles] = useState([]);
      const [nextArticleNum , setNextArticleNum ] = useState(0)
    
      useEffect(() => {
        const getArticles = async () => {
          const articlesRes = await axios.post("link-to-articles", { nextArticleNum: nextArticleNum })
          if(articlesRes.length <= 0) return;
          setArticles([...articles, ...articlesRes.data.articles])
          setNextArticleNum(nextArticleNum  + 20);
        };
    
        getArticles();
      }, [nextArticleNum]);
    Login or Signup to reply.
  2. I haven’t run the code I wrote, but I think I can write it this way.

    
    const [articles, setArticles] = useState([]);
    
    const getArticles = async (nextArticleNum) => {
        const articlesRes = await axios.post("link-to-articles", { nextArticleNum: nextArticleNum });
        setArticles((value) => {
          const data = articlesRes.data.articles ?? [];
          return [...value, ...data];
        });
    }
    
    useEffect(() => {
        getArticles(0);
    }, []);
    
    
    useEffect(() => {
      const nextArticleNum = Math.floor(articles / 100);
      if(nextArticleNum < 10) {
        getArticles(nextArticleNum);
      }
    }, [articles])
    
    Login or Signup to reply.
  3. Firstly – I think your infinite death loop is caused by this line:

    if (articles.length === 0)
    

    it probably should have been

    if (articlesRes.data.articles.length === 0)
    

    as this would stop the loop if no results were returned (bounds exceeded)

    Secondly – other than reducing the size of the axios resultset you are not really gaining anything by breaking this into 100 rec chucks as the constant state updates will cause the component to repeatably refresh until all records are retrieved – likely interrupting user interaction within the UI.

    I would suggest using an approach like virtual/infinite scrolling. The gist of this approach is, after you get the initial page of data you only get further records when you need it i.e. as it would scroll into view.

    Here’s an article which describes the approach and includes an example which you could alter to fit your needs. There are also a number of libraries which provide this functionality.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search