Create a NEWS App using Next JS and React Query

thumbnail

Hello Friends, Today in this post we will learn how to create a news web app using NEXT JS and a popular react package react-query. So we will use newsapi.org and the react-query package for fetching this data. By doing this web app you will learn about API fetching in NEXT JS and some basic usage of react-query package. Also, we have implemented pagination while fetching API. So let's get started.

React Query provides state management for the synchronous data. It provides features such as caching, background fetching, etc. It also helps us in configuring the number of unsuccessful retries in fetching API. You can refer react-query documentation for more information.

Overview | React Query | TanStack

Step 1 — Project Setup

Create a new next js project using

npm create-next-app news-app

Once created, install the react-query package using

npm install react-query

Step 2 — Code

Open _app.js, import QueryClient, QueryClientProvider from react-query and wrap your Component under QueryClientProvider, this QueryClientProvider requires client prop so for that we will create a useState variable queryClient which value as new QueryClient().

Now in index.js, we will create a function to fetch response from newsapi

const getNewsData = async () => {
  let page = queryKey[1];
  let res = await fetch(
    `https://newsapi.org/v2/top-headlines?country=in&category=business&apiKey=${process.env.NEWS_API_KEY}`
  );
  return res.json();
};
export default function Home() {
  return <div></div>
}

To use this we have useQuery hook provided by the react-query package. This useQuery requires a key of string and a function that returns a response from API in return we will get an object containing isLoading, isSuccess, isError, data, etc

Now we will map different components for isLoading, isError, and finally this data to a component that shows news title and description

import { useQuery } from "react-query";
import { useState } from "react";

...

export default function Home() {
  const [page, setPage] = useState(1);

  const { isLoading, isError, isSuccess, data } = useQuery(
    "news",
    getNewsData
  );

  if (isLoading) return <div className="center">Loading...</div>;
  else if (isError)
    return (
      <div className="center">Something went wrong, Please try again.</div>
    );
  return (
    <div className="container">
      <h1 className="text-center">News App</h1>
      {data.articles.map((d) => (
        <div className="news-card" key={d.title}>
          <h2>{d.title}</h2>
          <p>{d.description}</p>
        </div>
      ))}   
    </div>
  );
}

By default, Newsapi gives only 20 results per page, so to work with different pages we need a state variable page to fetch around different pages, in our previous useQuery hook, we will wrap a news string inside an array and add a page variable inside the array. To use this page value inside our getNewsData function we will get from queryKey from the index 1. If you want to know more about the values passed try console log the parameter without destructuring. Add a page parameter to API URL and make it equal to the value at queryKey at index 1.

...
const getNewsData = async ({ queryKey }) => {
  let page = queryKey[1];
  let res = await fetch(
    `https://newsapi.org/v2/top-headlines?page=${page}&country=in&category=business&apiKey=${process.env.NEWS_API_KEY}`
  );
  return res.json();
};

export default function Home() {
  const [page, setPage] = useState(1);

  const { isLoading, isError, isSuccess, data } = useQuery(
    ["news", page],
    getNewsData
  );
  ....
}

Now for implementing the next and previous page we will increment and decrement the page value, but before incrementing we will check for a condition that ceil value for totalResults / 20 so if our totalResults is 70 then ceiled result will be 4, reason for using ceil is if we have used floor then result would turn out to be 3, and decrementing we will just check whether our current page is greater > 1.

...
export default function Home() {
   ...
   return (
    <div className="container">
      <h1 className="text-center">News App</h1>
      <div className="actions">
        <button
          onClick={() => {
            if (page > 1) setPage(page - 1);
          }}
        >
          &larr;
        </button>
        <button
          onClick={() => {
            if (page < Math.ceil(data.totalResults / 20)) setPage(page + 1);
          }}
        >
          &rarr;
        </button>
      </div>
      {data.articles.map((d) => (
        <div className="news-card" key={d.title}>
          <h2>{d.title}</h2>
          <p>{d.description}</p>
        </div>
      ))}
      <div></div>
    </div>
);
}

Finally, add styles for the components.

html,
body {
  padding: 0;
  margin: 0;
  font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif, sans-serif;
}

a {
  color: inherit;
  text-decoration: none;
}

* {
  box-sizing: border-box;
}
.container{
  max-width: 768px;
  margin: auto;
}
.text-center{
  text-align: center;
}
.center{
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 600px;
}
h2{
  font-size: 20px;
  font-weight: bold;
}
p{
  font-size: 18px;
  font-weight: 400;
  color: #333;
  line-height: 24px;
}
.actions{
  display: flex;justify-content: space-between;
}
.news-card{
  border-bottom: 1px solid #eee;
  padding: 16px;
}

Thanks for reading this post, if you found this post helpful please share maximum, Thanks for reading 😊 Stay tuned.

You can refer the News API documentation to explore the available data.

Documentation - News API

You can find the code repository on Github

https://github.com/CodeWithMarish/news-app

If you are facing any issues please contact us from our contact section.

Contact Us | CodeWithMarish

Also please don’t forget to subscribe to our youtube channel codewithmarish for all web development-related challenges.

Code With Marish | Youtube

Related Posts