diff --git a/src/app/(AppBarHeader)/toks-main/_components/QuizListServerComponent.tsx b/src/app/(AppBarHeader)/toks-main/_components/QuizListServerComponent.tsx new file mode 100644 index 000000000..e98862c18 --- /dev/null +++ b/src/app/(AppBarHeader)/toks-main/_components/QuizListServerComponent.tsx @@ -0,0 +1,32 @@ +import { + HydrationBoundary, + QueryClient, + dehydrate, +} from '@tanstack/react-query'; +import React from 'react'; + +import { CardList } from './CardList'; +import { getQuizList } from '../_lib/remotes/quiz'; + +const QuizListServerComponent = async () => { + const queryClient = new QueryClient(); + await queryClient.prefetchInfiniteQuery({ + queryKey: ['posts', 'recommends'], + queryFn: ({ pageParams }) => + getQuizList({ + page: pageParams, + size: 10, + categoryIds: [1, 2, 3, 4, 5, 6, 7, 8, 9], + }), + initialPageParam: 0, + }); + const dehydratedState = dehydrate(queryClient); + + return ( + + + + ); +}; + +export default QuizListServerComponent; diff --git a/src/app/(AppBarHeader)/toks-main/_lib/remotes/quiz.ts b/src/app/(AppBarHeader)/toks-main/_lib/remotes/quiz.ts new file mode 100644 index 000000000..faab01b86 --- /dev/null +++ b/src/app/(AppBarHeader)/toks-main/_lib/remotes/quiz.ts @@ -0,0 +1,21 @@ +type Props = { page: number; size: number; categoryIds: string[] }; + +export const getQuizList = async ({ page, size, categoryIds }: Props) => { + const queryParams: Record = {}; + + if (categoryIds.length > 0) { + queryParams['categoryIds'] = categoryIds.join(','); + } + + queryParams['page'] = String(page); + queryParams['size'] = String(size); + + const searchParams = new URLSearchParams(queryParams).toString(); + const res = await fetch(`api/v1/quizzes?${searchParams}`, { + next: { + tags: ['quiz-list'], + }, + }); + + return res.json(); +}; diff --git a/src/common/utils/isEqual.ts b/src/common/utils/isEqual.ts new file mode 100644 index 000000000..31a6a4bc0 --- /dev/null +++ b/src/common/utils/isEqual.ts @@ -0,0 +1,45 @@ +export const isEqual = (value: unknown, other: unknown): boolean => { + if (value === other) { + return true; + } + + if (typeof value !== typeof other) { + return false; + } + + if (Array.isArray(value) && Array.isArray(other)) { + if (value.length !== other.length) { + return false; + } + for (let i = 0; i < value.length; i++) { + if (!isEqual(value[i], other[i])) { + return false; + } + } + return true; + } + + if ( + typeof value === 'object' && + typeof other === 'object' && + value !== null && + other !== null + ) { + const valueObj = value as Record; + const otherObj = other as Record; + const valueKeys = Object.keys(valueObj); + const otherKeys = Object.keys(otherObj); + + if (valueKeys.length !== otherKeys.length) { + return false; + } + + return valueKeys.every( + (key) => + Object.prototype.hasOwnProperty.call(otherObj, key) && + isEqual(valueObj[key], otherObj[key]) + ); + } + + return value === other; +}; diff --git a/src/common/utils/reactQuery.ts b/src/common/utils/reactQuery.ts new file mode 100644 index 000000000..0be1199d4 --- /dev/null +++ b/src/common/utils/reactQuery.ts @@ -0,0 +1,55 @@ +import { + HydrationBoundary, + QueryClient, + QueryKey, + QueryState, + dehydrate, +} from '@tanstack/react-query'; +import { cache } from 'react'; + +import { isEqual } from './isEqual'; + +export const getQueryClient = cache(() => new QueryClient()); + +type UnwrapPromise = T extends Promise ? U : T; + +interface QueryProps { + queryKey: QueryKey; + queryFn: () => Promise; +} + +interface DehydratedQueryExtended { + state: QueryState; +} + +export async function getDehydratedQuery({ + queryKey, + queryFn, +}: Q) { + const queryClient = getQueryClient(); + await queryClient.prefetchQuery({ queryKey, queryFn }); + + const { queries } = dehydrate(queryClient); + const [dehydratedQuery] = queries.filter((query) => + isEqual(query.queryKey, queryKey) + ); + + return dehydratedQuery as DehydratedQueryExtended< + UnwrapPromise> + >; +} + +export async function getDehydratedQueries(queries: Q) { + const queryClient = getQueryClient(); + await Promise.all( + queries.map(({ queryKey, queryFn }) => + queryClient.prefetchQuery({ queryKey, queryFn }) + ) + ); + + return dehydrate(queryClient).queries as Array< + DehydratedQueryExtended>> + >; +} + +export const Hydrate = HydrationBoundary;