0

I have a component in which I am making a http request to get some news. Once I receive news then I call sortNewsStories function to sort them based on its dates. I am using a sort() function to do that but somehow, it is not working. It is not sorting the news and sometimes it returns empty array. Here is my code.

import { useState, useEffect } from "react";
import "./styles.css";

export default function App() {
  const [topNews, setTopNews] = useState([]);
  const apiKey = "test key";
  const baseUrl = "https://content.guardianapis.com/";

  useEffect(() => {
    async function fetchData() {
      const news = await fetchNews("news", 15); // I calling fetchNews method which makes https request.
      setTopNews(news.response.results); // This set topNews to results received from http request.
      sortNewsStories("Newest first"); // Here I am sorting news/
    }
    fetchData();
  }, []);
  console.log(topNews); // This returns unsorted array of object which is expected.
  const sortNewsStories = (order) => {
    topNews.sort((a, b) => { // Here I am using sort method to sort the array of objects
      if (order === "Newest first") { // check if order is newest then sort it accordingly.
        return new Date(b.webPublicationDate) - new Date(a.webPublicationDate);
      } else {
        return new Date(a.webPublicationDate) - new Date(b.webPublicationDate);
      }
    });
    setTopNews(topNews); // set topNews again with sorted topNews array which is not working and sometimes it becomes empty array with no news in it and sometimes it has objects with news in it but not sorted.
    console.log("SORTED: ", topNews); // Here is shows either empty or unsorted array.
  };

  const fetchNews = async (section, pageSize) => {
    const url = `${baseUrl}${section}?show-sections=true&page-size=${pageSize}&api-key=${apiKey}`;
    const news = await fetch(url);
    return await news.json();
  };

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

Here is the codesandbox for this code.

https://codesandbox.io/s/inspiring-cray-mq7bo?file=/src/App.js:0-1228

Om3ga
  • 30,465
  • 43
  • 141
  • 221

2 Answers2

3

This is a common pitfall in react: setX from useState does not set X right away, it's asynchronous.

But in your case you can simplify your code and sort immediately before setting the variable:

useEffect(() => {
    async function fetchData() {
      const news = await fetchNews("news", 15);
      setTopNews(news.response.results.sort((a, b) => {
        if (order === "Newest first") {
          return new Date(b.webPublicationDate) - new Date(a.webPublicationDate);
        } else {
          return new Date(a.webPublicationDate) - new Date(b.webPublicationDate);
        }
      }));
    }
    fetchData();
  }, []);
Christian Fritz
  • 20,641
  • 3
  • 42
  • 71
  • I need it in a function because I have a dropdown select in which I will sort `topNews` according to the value selected in select dropdown. That means I need to be keep calling sort function based on select dropdown. Sorting right away would not solve my use case. – Om3ga Jul 10 '21 at 02:47
1

Another solution is to use a callback inside setState like this:

  const sortNewsStories = (order) => {
    setTopNews((unorderedNews) =>
      unorderedNews.sort((a, b) => {
        if (order === "Newest first") {
          return (
            new Date(b.webPublicationDate) - new Date(a.webPublicationDate)
          );
        } else {
          return (
            new Date(a.webPublicationDate) - new Date(b.webPublicationDate)
          );
        }
      })
    );
  };

But I think it would be better to set your state only once on the useEffect hook like the next example:

  useEffect(() => {
    async function fetchData() {
      const news = await fetchNews("news", 15);
      const orderedNews = sortNewsStories(
        "Newest first",
        news.response.results
      );

      setTopNews(orderedNews);
    }
    fetchData();
  }, []);

  const sortNewsStories = (order, data) => {
    return data.sort((a, b) => {
      if (order === "Newest first") {
        return new Date(b.webPublicationDate) - new Date(a.webPublicationDate);
      } else {
        return new Date(a.webPublicationDate) - new Date(b.webPublicationDate);
      }
    });
  };