diff --git a/components/HotelReservation/SelectRate/HotelInfoCard/NoRoomsAlert.tsx b/components/HotelReservation/SelectRate/HotelInfoCard/NoRoomsAlert.tsx index 37653efd2..6896b0c37 100644 --- a/components/HotelReservation/SelectRate/HotelInfoCard/NoRoomsAlert.tsx +++ b/components/HotelReservation/SelectRate/HotelInfoCard/NoRoomsAlert.tsx @@ -1,6 +1,6 @@ import { Lang } from "@/constants/languages" import { dt } from "@/lib/dt" -import { getRoomAvailability } from "@/lib/trpc/memoizedRequests" +import { getRoomsAvailability } from "@/lib/trpc/memoizedRequests" import Alert from "@/components/TempDesignSystem/Alert" import { getIntl } from "@/i18n" @@ -31,7 +31,7 @@ export async function NoRoomsAlert({ lang, }: Props) { const [availability, availabilityError] = await safeTry( - getRoomAvailability({ + getRoomsAvailability({ hotelId: hotelId, roomStayStartDate: dt(fromDate).format("YYYY-MM-DD"), roomStayEndDate: dt(toDate).format("YYYY-MM-DD"), diff --git a/components/HotelReservation/SelectRate/Rooms/RoomsContainer.tsx b/components/HotelReservation/SelectRate/Rooms/RoomsContainer.tsx index c46ebbf25..2001cf722 100644 --- a/components/HotelReservation/SelectRate/Rooms/RoomsContainer.tsx +++ b/components/HotelReservation/SelectRate/Rooms/RoomsContainer.tsx @@ -4,7 +4,7 @@ import { getHotelData, getPackages, getProfileSafely, - getRoomAvailability, + getRoomsAvailability, } from "@/lib/trpc/memoizedRequests" import { safeTry } from "@/utils/safeTry" @@ -57,7 +57,7 @@ export async function RoomsContainer({ ) const roomsAvailabilityPromise = safeTry( - getRoomAvailability({ + getRoomsAvailability({ hotelId: hotelId, roomStayStartDate: fromDateString, roomStayEndDate: toDateString, diff --git a/lib/trpc/memoizedRequests/index.ts b/lib/trpc/memoizedRequests/index.ts index 1380517e3..d23689e16 100644 --- a/lib/trpc/memoizedRequests/index.ts +++ b/lib/trpc/memoizedRequests/index.ts @@ -1,11 +1,12 @@ -import { cache } from "react" - import { Lang } from "@/constants/languages" import { GetRoomsAvailabilityInput, GetSelectedRoomAvailabilityInput, + HotelDataInput, } from "@/server/routers/hotels/input" +import { cache } from "@/utils/cache" + import { serverClient } from "../server" import type { @@ -59,46 +60,20 @@ export const getUserTracking = cache(async function getMemoizedUserTracking() { return serverClient().user.tracking() }) -export const getHotelData = cache(async function getMemoizedHotelData({ - hotelId, - language, - isCardOnlyPayment, -}: { - hotelId: string - language: string - isCardOnlyPayment?: boolean -}) { - return serverClient().hotel.hotelData.get({ - hotelId, - language, - isCardOnlyPayment, - }) +export const getHotelData = cache(function getMemoizedHotelData( + props: HotelDataInput +) { + return serverClient().hotel.hotelData.get(props) }) -export const getRoomAvailability = cache( - async function getMemoizedRoomAvailability({ - hotelId, - adults, - roomStayStartDate, - roomStayEndDate, - children, - bookingCode, - rateCode, - }: GetRoomsAvailabilityInput) { - return serverClient().hotel.availability.rooms({ - hotelId, - adults, - roomStayStartDate, - roomStayEndDate, - children, - bookingCode, - rateCode, - }) - } -) +export const getRoomsAvailability = cache(function getMemoizedRoomAvailability( + args: GetRoomsAvailabilityInput +) { + return serverClient().hotel.availability.rooms(args) +}) export const getSelectedRoomAvailability = cache( - async function getMemoizedRoomAvailability( + function getMemoizedSelectedRoomAvailability( args: GetSelectedRoomAvailabilityInput ) { return serverClient().hotel.availability.room(args) @@ -141,13 +116,13 @@ export const getSiteConfig = cache(async function getMemoizedSiteConfig() { return serverClient().contentstack.base.siteConfig() }) -export const getBreakfastPackages = cache(async function getMemoizedPackages( +export const getBreakfastPackages = cache(function getMemoizedBreakfastPackages( input: BreackfastPackagesInput ) { return serverClient().hotel.packages.breakfast(input) }) -export const getPackages = cache(async function getMemoizedPackages( +export const getPackages = cache(function getMemoizedPackages( input: PackagesInput ) { return serverClient().hotel.packages.get(input) diff --git a/package-lock.json b/package-lock.json index 87aa057cc..b645f1f83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,6 +41,7 @@ "graphql-request": "^6.1.0", "graphql-tag": "^2.12.6", "immer": "10.1.1", + "json-stable-stringify-without-jsonify": "^1.0.1", "libphonenumber-js": "^1.10.60", "next": "^14.2.18", "next-auth": "^5.0.0-beta.19", @@ -64,6 +65,7 @@ "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", + "@types/json-stable-stringify-without-jsonify": "^1.0.2", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", @@ -6850,6 +6852,12 @@ "parse5": "^7.0.0" } }, + "node_modules/@types/json-stable-stringify-without-jsonify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.2.tgz", + "integrity": "sha512-X/Kn5f5fv1KBGqGDaegrj72Dlh+qEKN3ELwMAB6RdVlVzkf6NTeEnJpgR/Hr0AlpgTlYq/Vd0U3f79lavn6aDA==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -13939,8 +13947,7 @@ "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" }, "node_modules/json-stringify-safe": { "version": "5.0.1", diff --git a/package.json b/package.json index effaf858e..38301e51a 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "graphql-request": "^6.1.0", "graphql-tag": "^2.12.6", "immer": "10.1.1", + "json-stable-stringify-without-jsonify": "^1.0.1", "libphonenumber-js": "^1.10.60", "next": "^14.2.18", "next-auth": "^5.0.0-beta.19", @@ -79,6 +80,7 @@ "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", + "@types/json-stable-stringify-without-jsonify": "^1.0.2", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", diff --git a/server/routers/hotels/query.ts b/server/routers/hotels/query.ts index 910044af8..a293bc0b9 100644 --- a/server/routers/hotels/query.ts +++ b/server/routers/hotels/query.ts @@ -1,16 +1,11 @@ import { metrics } from "@opentelemetry/api" -import { cache } from "react" import { Lang } from "@/constants/languages" import * as api from "@/lib/api" import { dt } from "@/lib/dt" import { GetHotelPage } from "@/lib/graphql/Query/HotelPage/HotelPage.graphql" import { request } from "@/lib/graphql/request" -import { - badRequestError, - notFound, - serverErrorByStatus, -} from "@/server/errors/trpc" +import { badRequestError, notFound } from "@/server/errors/trpc" import { contentStackUidWithServiceProcedure, publicProcedure, @@ -20,6 +15,8 @@ import { } from "@/server/trpc" import { toApiLang } from "@/server/utils" +import { cache } from "@/utils/cache" + import { hotelPageSchema } from "../contentstack/hotelPage/output" import { fetchHotelPageRefs, @@ -57,7 +54,6 @@ import { import { FacilityCardTypeEnum } from "@/types/components/hotelPage/facilities" import type { BedTypeSelection } from "@/types/components/hotelReservation/enterDetails/bedType" -import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel" import { BreakfastPackageEnum } from "@/types/enums/breakfast" import { HotelTypeEnum } from "@/types/enums/hotelType" import type { RequestOptionsWithOutBody } from "@/types/fetch" diff --git a/utils/cache.ts b/utils/cache.ts new file mode 100644 index 000000000..2b44dee49 --- /dev/null +++ b/utils/cache.ts @@ -0,0 +1,22 @@ +import stringify from "json-stable-stringify-without-jsonify" +import { cache as reactCache } from "react" + +/** + * Wrapper function to handle caching of memoized requests that recieve objects as args. + * React Cache will use shallow equality of the arguments to determine if there is a cache hit, + * therefore we need to stringify the arguments to ensure that the cache works as expected. + * This function will handle the stingification of the arguments, the caching of the function, + * and the parsing of the arguments back to their original form. + * + * @param fn - The function to memoize + */ +export function cache any>(fn: T) { + const cachedFunction = reactCache((stringifiedParams: string) => { + return fn(...JSON.parse(stringifiedParams)) + }) + + return (...args: Parameters): ReturnType => { + const stringifiedParams = stringify(args) + return cachedFunction(stringifiedParams) + } +}