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
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
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);
}}
>
←
</button>
<button
onClick={() => {
if (page < Math.ceil(data.totalResults / 20)) setPage(page + 1);
}}
>
→
</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.
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.
Also please don’t forget to subscribe to our youtube channel codewithmarish for all web development-related challenges.