set metadata in server components
Unanswered
Sage Thrasher posted this in #help-forum
Sage ThrasherOP
export default async function Page({ params }: { params: { id: string } }) {
const data = await fetchProfile({ id: params.id }).catch(() => null);
if (!data) return;
// metadata title as 'data.name'how can i make the page title 'loading' then once the fetch is complete, it is set to data.name
47 Replies
@Sage Thrasher tsx
export default async function Page({ params }: { params: { id: string } }) {
const data = await fetchProfile({ id: params.id }).catch(() => null);
if (!data) return;
// metadata title as 'data.name'
how can i make the page title 'loading' then once the fetch is complete, it is set to data.name
you can directly generate the metadata so there won't be any loading state, because it's instantly loaded. See here: https://nextjs.org/docs/app/api-reference/functions/generate-metadata
@B33fb0n3 you can directly generate the metadata so there won't be any loading state, because it's instantly loaded. See here: https://nextjs.org/docs/app/api-reference/functions/generate-metadata
Sage ThrasherOP
i saw this, but then i would have to refetch the data in this function
@Sage Thrasher i saw this, but then i would have to refetch the data in this function
you can use the react cache so the request will be deduped. Like that you make one request and the result will be used twice (or more times)
@B33fb0n3 you can use the react cache so the request will be deduped. Like that you make one request and the result will be used twice (or more times)
Sage ThrasherOP
yeah i was thinking that but i didn't know if there was a more efficent way to parse the data
@Sage Thrasher yeah i was thinking that but i didn't know if there was a more efficent way to parse the data
it's the recommended way on fetching stuff. Read here: https://nextjs.org/docs/app/building-your-application/data-fetching/patterns#fetching-data-where-its-needed
Or here: https://nextjs.org/docs/app/building-your-application/caching#request-memoization
Or here: https://nextjs.org/docs/app/building-your-application/caching#request-memoization
... without worrying about the performance implications of making multiple requests for the same data.
@B33fb0n3 it's the recommended way on fetching stuff. Read here: https://nextjs.org/docs/app/building-your-application/data-fetching/patterns#fetching-data-where-its-needed
Or here: https://nextjs.org/docs/app/building-your-application/caching#request-memoization
> ... without worrying about the performance implications of making multiple requests for the same data.
Sage ThrasherOP
@B33fb0n3 i use caching on most my fetches, however when a user logs into the website for the first time with their discord account, thats the only time i get their data, is there a similar function on authjs to the session one but only calls once per browsing session or something similar so i can actively refetch their new data without using up a lot of storage for cached data or getting ratelimited by discord
Sage ThrasherOP
also in addition, say 3 people have loaded a page and therefore have data cached for an hour, but someone does something to update the data, how can i make the data be refetched and the page reupdated?
@Sage Thrasher also in addition, say 3 people have loaded a page and therefore have data cached for an hour, but someone does something to update the data, how can i make the data be refetched and the page reupdated?
you can use [unstable_cache](https://nextjs.org/docs/app/api-reference/functions/unstable_cache) to be able to cache third party (discord api) results directly and cache them for x time. If you call the discord api via [fetch](https://nextjs.org/docs/app/api-reference/functions/fetch) you don't need to use the unstable_cache. Inside the fetch you can directly revalidate after x seconds like:
// This request should be cached with a lifetime of 10 seconds.
const revalidatedData = await fetch(`https://...`, {
next: { revalidate: 10 },
})@B33fb0n3 you can use [unstable_cache](https://nextjs.org/docs/app/api-reference/functions/unstable_cache) to be able to cache third party (discord api) results directly and cache them for x time. If you call the discord api via [fetch](https://nextjs.org/docs/app/api-reference/functions/fetch) you don't need to use the unstable_cache. Inside the fetch you can directly revalidate after x seconds like:
tsx
// This request should be cached with a lifetime of 10 seconds.
const revalidatedData = await fetch(`https://...`, {
next: { revalidate: 10 },
})
Sage ThrasherOP
how could i forcefully revalidate this specific fetch?
const revalidatedData = await fetch(`https://...`, {
next: {
revalidate: 10,
tags: ['test']
}
});
revalidateTag('test')when the user clicks submit, a function calls a server action which posts data to my other api
once thats done, i want to only refresh the data for a specific fetch on the page
right now, i have to wait until revalidation timer and refresh
but i dont want to wait or refresh
Sage ThrasherOP
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'authorization': process.env.API_KEY,
'content-type': 'application/json'
},
body: JSON.stringify({
...patchedData,
userId: session.user.discordId
})
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(parseJson(errorText)?.message ?? response?.statusText ?? 'Something went wrong');
}
revalidateTag('fetchProfileReviews');
revalidatePath(`/profile/${profile.id}`);
return;this is what im currently doing but it doesn't refresh the data on the page (this is inside the server action which i call from a client component)
once it's completed, you can do a router.refresh() from where you called the server action
@Arinji once it's completed, you can do a router.refresh() from where you called the server action
Sage ThrasherOP
okay so the data refreshes on the page, however new elements aren't inserted using this
im making a comment feature where you can comment on the profile, once someone comments, the numbers (stats) increases however the comment isn't displayed unless i refresh the page
im making a comment feature where you can comment on the profile, once someone comments, the numbers (stats) increases however the comment isn't displayed unless i refresh the page
Sage ThrasherOP
here another example, you can see on initial load, the data is fetched then here is logged, after
router.refresh() here is logged again but the data isnti presume i need to put a dependency in the useEffect then change this value when i refresh so this useeffect is ran again?
@Sage Thrasher okay so the data refreshes on the page, however new elements aren't inserted using this
im making a comment feature where you can comment on the profile, once someone comments, the numbers (stats) increases however the comment isn't displayed unless i refresh the page
R u using a client side fetching library like SWR or react query?
@B33fb0n3 R u using a client side fetching library like SWR or react query?
Sage ThrasherOP
im just using fetch from next
@Sage Thrasher im just using fetch from next
Use a clientside fetching library to handle comments. Or only add the new comment clientside to display it
@B33fb0n3 Use a clientside fetching library to handle comments. Or only add the new comment clientside to display it
Sage ThrasherOP
what difference would this make ?
im just wondering
i think the issue is because my useeffect isnt emitting on refresh
@Sage Thrasher what difference would this make ?
with clientside fetching libraries you can control your data easily. Without it is still possible, but way harder
Sage ThrasherOP
which would you recommend?
also i am refreshing in a different component to where i am fetching
I like to use react query, because it has more functionallity then swr
Sage ThrasherOP
export async function ProfileBody(...) {
...
return (
<LeaveComment guildId={props.profile.id} />
<Comments guildId={props.profile.id} totalReviews={reviews} />
)
}// LeaveComment.tsx (where you send comments)
export default function LeaveComment(...) {
const router = useRouter();
const { data: session } = useSession();
const [rating, setRating] = useState<number>(0);
const [text, setText] = useState<string>('');
const [error, setError] = useState<string>();
const [state, setState] = useState<string>('default');
if (!session) return <></>;
async function handleSendComment() {
if (!props.reply && (rating <= 0 || rating > 5)) return setError('Rating must be between 1 and 5');
if (typeof text !== "string" || text.trim().length <= 10) return setError('Content must be at least 10 characters');
setError(undefined);
setState('loading');
try {
await createReview({ text, rating, profileId });
router.refresh();
toast.success('Successful', defaultToastOptions);
} catch (error: any) {
toast.error(error?.message ?? 'Something went wrong', defaultToastOptions);
}
setState('default');
}// Comments.tsx (visual render of all comments)
export default function Comments(...) {
const reviewRenderAmount = 5;
const { data: session } = useSession();
const [reviews, setReviews] = useState<ReviewDataResult[]>([]);
const [likedReviews, setLikedReviews] = useState<Set<string>>(new Set());
const [currentPage, setCurrentPage] = useState(1);
const [targetReply, setTargetReply] = useState<string>('');
const [totalPages, setTotalPages] = useState(Math.ceil(totalReviews / reviewRenderAmount));
const [loading, setLoading] = useState(false);
useEffect(() => {
setTotalPages(Math.ceil(totalReviews / reviewRenderAmount));
}, [totalReviews]);
useEffect(() => {
const fetchReviews = async () => {
setLoading(true);
try {
const result = await fetchProfileReviews(profileId, {
offset: (currentPage - 1) * reviewRenderAmount,
limit: reviewRenderAmount,
});
setReviews(result);
} finally {
setLoading(false);
}
};
fetchReviews();
}, [guildId, currentPage]);
}this is the general design,
LeaveComment is also referenced inside Comments for replying to commentsyea, use a clientside fetching library and then revalidate the data when the user commented. One example on revalidating data after something changes is create from me here: https://github.com/B33fb0n3/rq-profile-picture
Sage ThrasherOP
which file should i look at?
Sage ThrasherOP
also, would clientside fetching make the useEffect emit again because thats what's not happening right now
im using server actions to fetch the data as well so preferably would like to keep using them instead of the clientside fetching for the ease
yea, clientside fetching would take all the control over your data and you can easily manage it. Do you see anywhere in my project an useeffect for data fetching? 🙂
Sage ThrasherOP
ah
@B33fb0n3 yea, clientside fetching would take all the control over your data and you can easily manage it. Do you see anywhere in my project an useeffect for data fetching? 🙂
Sage ThrasherOP
is it possible to call a server action with react-query or swr?
@Sage Thrasher is it possible to call a server action with react-query or swr?
yes, can you please take a look at my project?
@B33fb0n3 yes, can you please take a look at my project?
Sage ThrasherOP
my fault, i was looking at the docs and saw they were using links
@Sage Thrasher tsx
// Comments.tsx (visual render of all comments)
export default function Comments(...) {
const reviewRenderAmount = 5;
const { data: session } = useSession();
const [reviews, setReviews] = useState<ReviewDataResult[]>([]);
const [likedReviews, setLikedReviews] = useState<Set<string>>(new Set());
const [currentPage, setCurrentPage] = useState(1);
const [targetReply, setTargetReply] = useState<string>('');
const [totalPages, setTotalPages] = useState(Math.ceil(totalReviews / reviewRenderAmount));
const [loading, setLoading] = useState(false);
useEffect(() => {
setTotalPages(Math.ceil(totalReviews / reviewRenderAmount));
}, [totalReviews]);
useEffect(() => {
const fetchReviews = async () => {
setLoading(true);
try {
const result = await fetchProfileReviews(profileId, {
offset: (currentPage - 1) * reviewRenderAmount,
limit: reviewRenderAmount,
});
setReviews(result);
} finally {
setLoading(false);
}
};
fetchReviews();
}, [guildId, currentPage]);
}
Sage ThrasherOP
on this last example, im using use effect to re-fetch once the current page changes because im using pagination, how can i do something similar with react-query?
The questions, that you currently asking are already answered in the repo that I linked. See here:
Server actions: https://github.com/B33fb0n3/rq-profile-picture/blob/main/app/ChangeProfilePictureButton.tsx#L13
Revalidation: https://github.com/B33fb0n3/rq-profile-picture/blob/main/app/ChangeProfilePictureButton.tsx#L20
Server actions: https://github.com/B33fb0n3/rq-profile-picture/blob/main/app/ChangeProfilePictureButton.tsx#L13
Revalidation: https://github.com/B33fb0n3/rq-profile-picture/blob/main/app/ChangeProfilePictureButton.tsx#L20
@Sage Thrasher solved?