This commit is contained in:
nm1 2026-11-04 00:33:23 +05:30
commit 74ae93a96e

View file

@ -0,0 +1,486 @@
Coding 10 Data fetching
Data Fetching
- Server components support various configurations for caching, revalidating and optimizing data fetching
- Client components have to use third party libraries such as TanStack Query which offers robus APIs
Structure
```text
src/app
users
page.jsx
page.css
```
In `src/app/users/page.jsx`
```js
import "./page.css";
function User(props) {
const { id, name, username, email, phone } = props;
return (
<div key={id} className="user">
<div className="col1">
<div>{name}</div>
<div>{username} </div>
</div>
<div className="col2">
<div className="">{email}</div>
<div className="">{phone}</div>
</div>
</div>
);
}
export default async function UsersPage() {
console.log("UsersPage loaded");
const response = await fetch("https://jsonplaceholder.typicode.com/users");
const users = await response.json();
console.log(users);
return (
<div className="UsersPage">
{users.map((user) => (
<User
id={user.id}
name={user.name}
username={user.username}
email={user.email}
phone={user.phone}
/>
))}
</div>
);
}
```
In `src/app/users/page.css`
```css
body {
background: rgb(20, 20, 20);
}
.UsersPage {
text-align: left;
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 2rem;
padding: 4rem;
margin: auto;
}
.UsersPage .user {
min-width: 350px;
width: 60%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background: white;
margin: auto;
border: 1px solid grey;
border-radius: 5px;
}
.UsersPage .user .col1,
.UsersPage .user .col2 {
display: flex;
flex-direction: column;
/* align-items: center; */
flex-grow: 1;
}
.UsersPage .user .col2 {
align-items: end;
}
```
&nbsp;
**Loading and Error States**
Structure
```text
src/app
users
page.jsx
page.css
error.jsx
loading.jsx
```
In `@/users/page.jsx`
```js
import { resolve } from "styled-jsx/css";
import "./page.css";
function User(props) {
const { id, name, username, email, phone } = props;
return (
<div key={id} className="user">
<div className="col1">
<div className="font-bold">{name}</div>
<div>{username} </div>
</div>
<div className="col2">
<div className="">{email}</div>
<div className="">{phone}</div>
</div>
</div>
);
}
export default async function UsersPage() {
await new Promise((resolve) => setTimeout(resolve, 2000));
console.log("UsersPage loaded");
const response = await fetch("https://jsonplaceholder.typicode.com/users1");
const users = await response.json();
console.log(users);
return (
<div className="UsersPage">
{users.map((user) => (
<User
id={user.id}
name={user.name}
username={user.username}
email={user.email}
phone={user.phone}
/>
))}
</div>
);
}
```
In `@/users/page.css`
```css
body {
background: rgb(20, 20, 20);
}
.UsersPage {
text-align: left;
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 2rem;
padding: 4rem;
margin: auto;
}
.UsersPage .user {
min-width: 350px;
width: 60%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background: white;
margin: auto;
border: 1px solid grey;
border-radius: 5px;
}
.UsersPage .user .col1,
.UsersPage .user .col2 {
display: flex;
flex-direction: column;
/* align-items: center; */
flex-grow: 1;
}
.UsersPage .user .col2 {
align-items: end;
}
```
In `@/users/loading.jsx`
```js
export default function LoadingPage() {
return (
<div className="flex items-center justify-center h-screen">
<div className="animate-spin rounded-full h-32 w-32 border-t-2 border-b-2 border-white"></div>
</div>
);
}
```
In `@/users/error.jsx`
```js
"use client";
import { useEffect } from "react";
export default function ErrorPage({ error }) {
useEffect(() => {
console.error(`${error}`);
}, [error]);
return (
<div className="flex items-center justify-center h-screen">
<div className="text-2xl text-red-500">Error fetching users data</div>
</div>
);
}
```
&nbsp;
**JSON Server Setup**
- Install npm package `json-server@0.17.4`. Make sure that this is the version
- At the root of project create 2 files named `db.json` and `server.js`.
In `db.json`type
```json
{
"products": [
{
"id": 1,
"title": "Product 1",
"price": 800,
"description": "Description 1"
},
{
"id": 2,
"title": "Product 2",
"price": 1000,
"description": "Description 2"
},
{
"id": 3,
"title": "Product 3",
"price": 2500,
"description": "Description 3"
}
]
}
```
In `server.js`
```js
const jsonserver = require("json-server");
const server = jsonserver.create();
const PORT = 3002;
const router = jsonserver.router("./db.json");
const middlewares = jsonserver.defaults();
server.use(middlewares);
server.use((req, res, next) => {
console.log("request recieved");
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, PATCH, OPTIONS"
);
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
server.use(router);
server.listen(PORT, () => {
console.log("JSON Server is running");
});
```
As port is 3002 so when we visit to `localhost:3002/products`. we get array of products
&nbsp;
Caching Data
- By default, Next.js automatically catches the returned values of fetch in the Data Cache on the server.
In `src/app/products/page.jsx`
```js
import "./page.css";
export default async function Products() {
console.log("Products loaded");
const response = await fetch("http://localhost:3002/products");
const products = await response.json();
return (
<ul className="Products space-y-4 p-4">
{products.map((product) => (
<li className="p-4 bg-white shadow-md rounded-lg text-gray-700">
<h2 className="text-xl font-semibold">{product.title}</h2>
<p>{product.description}</p>
<p className="text-lg font-medium">{product.price}</p>
</li>
))}
</ul>
);
}
```
&nbsp;
**Data Cache**
- It is a server-side cache that persists the result of data fteches across incoming server requests and deployments
- It improves app performance and reduce costs by eliminating need to refetch data from data source with every request
- ![8d6701c74b6fe0c0c8e567dba819b36c.png](../../../../_resources/8d6701c74b6fe0c0c8e567dba819b36c.png)
- When request is made instead of getting information from data source it checks if its exist on cache. If it does exist then it simply returns it otherwise it will get data from source, send back to client also saving it in cache.
- This data cache is stored in `.next/cache`. One such is `fetch-cache` in this folder. It contains cached data of the routes.
- Unlike browser cache this is server cache means even if we load this page in different browser or incognito mode we get same result
- If we clear the cache file and restart json and next dev server then we will get updated value
&nbsp;
**Opt out of caching**
- To simply opt out of caching when fetching, set field `cookie` as `no-store`.
eg
```js
const response = await fetch("http://localhost:3002/products", {
cache: "no-store",
});
```
- When using `no-store` all subsequent fetch requests will also be out out of caching too so its wise to place those fetch requests where caching is needed to use before then those where caching is opted out
- There is a workaround to use cache even after using it after fetch request with `cache` set as `no-store`
Type the following at top of the code
`export const fetchCache = "default-cache";`
This is route level
https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#fetchcache
Eg: In `src/app/products/page.jsx`
```js
import "./page.css";
export const fetchCache = "default-cache";
export default async function Products() {
console.log("Products loaded");
const response = await fetch("http://localhost:3002/products", {
cache: "no-store",
});
const products = await response.json();
const detailedResponse = await fetch("http://localhost:3002/products/1");
const detailedData = await detailedResponse.json();
console.log("detailedata: ", detailedData);
return (
<ul className="Products space-y-4 p-4">
{products.map((product) => (
<li className="p-4 bg-white shadow-md rounded-lg text-gray-700">
<h2 className="text-xl font-semibold">{product.title}</h2>
<p>{product.description}</p>
<p className="text-lg font-medium">{product.price}</p>
</li>
))}
</ul>
);
}
```
Other values of fetchCache
```js
export const fetchCache = 'auto'
// 'auto' | 'default-cache' | 'only-cache'
// 'force-cache' | 'force-no-store' | 'default-no-store' | 'only-no-store'
```
- Another way is using dynamic functions ( cookies(), headers(), searchParams ). Make sure to use them before making fetch requests. fetch requests before using dynamic functions will be cached by dedault though
Eg:
```js
import { cookies } from "next/headers";
import "./page.css";
// export const fetchCache = "default-cache";
export default async function Products() {
console.log("Products loaded");
const cookieStore = cookies();
cookieStore.get("sd");
const detailedResponse = await fetch("http://localhost:3002/products/1");
const detailedData = await detailedResponse.json();
const response = await fetch("http://localhost:3002/products", {
cache: "no-store",
});
const products = await response.json();
console.log("detailedata: ", detailedData);
return (
<ul className="Products space-y-4 p-4">
{products.map((product) => (
<li className="p-4 bg-white shadow-md rounded-lg text-gray-700">
<h2 className="text-xl font-semibold">{product.title}</h2>
<p>{product.description}</p>
<p className="text-lg font-medium">{product.price}</p>
</li>
))}
</ul>
);
}
```
&nbsp;
**Request Memoization** (Client-side caching)
- It is a technique that deduplicates requests for the same data within a single render pass. This feature is by default
- This approach allows for re-use of data in a React Component tree, prevents redundant network calls
- After data is fetched initially, result is stored in memory
- When same data is needed across different components in a rout( eg in a layout, page and multiple components ), it eliminates the need to fetch data a the top of tree
Structure
```text
src/app
products
layout.jsx
page.jsx
```
In `src/app/products/layout.jsx`
```js
export default async function LayoutPage({ children }) {
const productresponse = await fetch("http://localhost:3002/products");
const data = await productresponse.json();
console.log("product data :", data);
console.log("Product LayoutPage loaded");
return <>{children};</>;
}
```
this url is fetched in page.jsx too but at json server this route is accessed one time only bcz layout.jsx and page.jsx is in single render pass
- ![f2ea8455f4a7eb8c141ec7f64752c652.png](../../../../_resources/f2ea8455f4a7eb8c141ec7f64752c652.png)
- When request is made it checks memory for the result. If not then it goes to server and in server it checks data cache then it checks data source.
&nbsp;
Note: Its not limited to client side bcz components can be server component too
&nbsp;
Imp notes about it
- Request memoization is a React future and not limited to nextjs. In React 19 request memization will no longer be default feature. So its possible then in future it will be no longer by default too
- Memoization only applies to GET method in fetch requests.
- It applies to react component tree and not extend to route handlers as they are not part of react component tree
- For cases where fetch is not suitable (eg some database clients, CMS clients or GraphQL clients), we can use the React cache function to meoize functions.