I'm having an optimization issue when I want to display dynamic title for the page and dynamic UI.
In my case I'm sending the same request 2 times - 1st time to generate metadata and 2nd time in order to pass it as a props to my UI component.
Previously, in Next.js with pages, I would solve it using getServerSideProps, but as long as this component anyways rendering on server, I didn't find any function; solution which would allow me to pass data from the server as a props for my functions (as it was when you were using pages router)
I use axios for querying data, initially I wanted to use RTK Query (and this problem would be solved because of caching), but for now RTK Query doesn't support React Server Components
My component.ts
file
import MyComponent from '@/components/screens/screen/component';
import { getDataSSR } from '@/components/screens/screen/api/screen.ssr';
import { IData } from '@/components/screens/screen/types/data.type';
export async function generateMetadata({ params }: { params: { id: string } }) {
const data = await getDataSSR({ id: Number(params?.id)}) as IData;
return {
title: `${data?.data?.details?.userProfile?.fullName}`,
};
}
const ComponentPage = async ({ params }: { params: { id: string } }) => {
const data = await getDataSSR({ id: Number(params?.id) }) as IData;
return <MyComponent id={params.id} data={data} />;
};
export default ComponentPage;
I tried to ask chat-gpt 4 about this, but unfortunately, he didn't provide me any valuable and working solution.
Problem :
I'm sending the same request 2 times - 1st time to generate metadata and 2nd time in order to pass it as a props to my UI component.
Solution :
cache
function provided by React, wrap your API Call function in cache function. (Read the 1st link given below)Here's a small code I made :
Folder Structure :
projectName
├── src
│ └── app
│ ├── api
│ ├── comp
│ │ └── User.js
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.js
│ └── user
│ ├── page.js
│ └── [id]
│ └── page.js
└── tailwind.config.js
Explaination :
I have made a page called Users, which shows all users.
I have made a component which takes data, & makes hyerlinks to open a specific user page (click on Terry's link, opens Terry's details page).
I have made a user details page, which calls a details api to show user details. This page has, generateMetadata
which calls a GetUserData
API & the page function also calls GetUserData
API .
[id]\page.js
is user details page
comp
is component folder
All Users Page projectName\src\app\user\page.js
:
import axios from "axios";
import User from "../comp/User";
async function GetAllUsers() {
let { data } = await axios.get('https://dummyjson.com/users')
return data
// CHANGE ABOVE CODE ACCORDING TO AXIOS
}
// ABOVE API SIMULATES SERVER-SIDE DATA FETCHING
export default async function Page() {
let UserData = await GetAllUsers();
// console.log("Data KEYS from API on Serverside : ", Object.keys(UserData));
// THIS LOG WILL BE IN TERMINAL AS THIS PAGE IS SERVER-SIDE RENDERED
return (
<div>
<h1>Users Page</h1>
<User Data={UserData} />
{/* COMPONENT */}
</div>
)
}
User Component : projectName\src\app\comp\User.js
'use client'
import Link from 'next/link'
import React, { useState } from 'react'
const User = ({ Data }) => {
// console.log("Data.users", Data.users);
// VISBILE IN BROWSER
const [UserData, SetUserData] = useState(Data.users)
return (
<div>
<h3>Client Component Gets data from Server as props, renders it below.</h3>
<ol>
{
UserData.map((u, i) => (
<li key={i}>
<Link href={`/user/` + u.id} >{u.firstName}</Link>
</li>
))
}
</ol>
</div>
)
}
export default User;
User details page projectName\src\app\user\[id]\page.js
:
import axios from 'axios';
import React from 'react'
import { cache } from 'react'
const GetUserData = cache(async (ID) => {
// console.log(ID);
console.log("GetUserData HIT (cache) : ", new Date().toLocaleTimeString());
let { data } = await axios.get('https://dummyjson.com/user/' + ID)
return data
})
// WITHOUT ANY CACHING , THIS RUNS TWICE
// UNCOMMENT BELOW CODE TO RUN & COMMENT ABOVE CODE
// const GetUserData = async (ID) => {
// // console.log(ID);
// console.log("GetUserData HIT : ", new Date().toLocaleTimeString());
// let { data } = await axios.get('https://dummyjson.com/user/' + ID)
// return data
// // CHANGE ABOVE CODE ACCORDING TO AXIOS
// }
export async function generateMetadata({ params }) {
let UserData = await GetUserData(params.id)
return {
title: UserData.firstName + " " + params.id,
};
}
const UserDetailsPage = async ({ params }) => {
let UserData = await GetUserData(params.id)
return (
<div>
<h1>UserDetailsPage </h1>
<p>ID:{params.id} </p>
<p>FirstName : {UserData.firstName}</p>
<p>LastName : {UserData.lastName}</p>
</div>
)
}
export default UserDetailsPage
Output :
Goto http://localhost:3000/user
you will see all users fetch from api.
Now click on anyone, it will take you to UserDetailsPage, http://localhost:3000/user/id_of_user
see this page has Title + User ID (made using generateMetadata function) this page has api 2 calls 1st uses cache & 2nd doesn't.
you will see in terminal it cache
wrapped api call executes only once. But if you comment this cache
wrapped api call & uncomment the below api call you see it fires twice.
Please Read :
Example which shows use of cache function : https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#example
Caching Data : https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#caching-data
Fetching data on the Server with third-party libraries : https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#fetching-data-on-the-server-with-third-party-libraries
Data Fetching, Caching, and Revalidating : https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating
Dynamic Routes : https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes
If you still have any doubts, leave a comment(i will update answer if required)