nextjs commerce 에서 SWR handler.useHook
이 호출되는 과정
packages/commerce/src/cart/use-cart.tsx
, useCart 에서 useSWRHook 호출packages/commerce/src/utils/use-hook.ts
, useSWRHook 에서 hook.useHook 호출packages/vendure/src/cart/use-cart.tsx
, handler.useHook
자세한 code
packages/commerce/src/cart/use-cart.tsx
의 useCart
를 보면, useSWRHook
를 호출하고 있다. 이 때 hook을 넘기는데, 이 hook 은 useHook
을 이용해서 얻는다. 아래 packages/commerce/src/utils/use-hook.ts
를 보면 된다.
이 useHook
은 parameter 로 fn 을 넘기는데, 이 fn 에서 어떤 hook 을 사용할 것인지 고른다고 보면 된다. 참고로, 이 useHook
과 hook.useHook
은 다른 함수다.
// packages/commerce/src/cart/use-cart.tsx
//
import Cookies from 'js-cookie'
import { useHook, useSWRHook } from '../utils/use-hook'
import type { SWRHook, HookFetcherFn } from '../utils/types'
import type { GetCartHook } from '../types/cart'
import { Provider, useCommerce } from '..'
export type UseCart<H extends SWRHook<GetCartHook> = SWRHook<GetCartHook>> =
ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<GetCartHook> = async ({
options,
input: { cartId },
fetch,
}) => {
return cartId ? await fetch(options) : null
}
const fn = (provider: Provider) => provider.cart?.useCart!
const useCart: UseCart = (input) => {
// hook 을 가져오는, 여기선 useCart 에 해당하는 hook 을 가져오기 위한 함수
const hook = useHook(fn)
const { cartCookie } = useCommerce()
const fetcherFn = hook.fetcher ?? fetcher
const wrapper: typeof fetcher = (context) => {
context.input.cartId = Cookies.get(cartCookie)
return fetcherFn(context)
}
return useSWRHook({ ...hook, fetcher: wrapper })(input)
}
export default useCart
packages/commerce/src/utils/use-hook.ts
를 보면, useSWRHook
이 있다. 여기서 hook.useHook
을 호출한다.
// packages/commerce/src/utils/use-hook.ts
//
import { useCallback } from 'react'
import { Provider, useCommerce } from '..'
import type { MutationHook, PickRequired, SWRHook } from './types'
import useData from './use-data'
export function useFetcher() {
const { providerRef, fetcherRef } = useCommerce()
return providerRef.current.fetcher ?? fetcherRef.current
}
export function useHook<
P extends Provider,
H extends MutationHook<any> | SWRHook<any>
>(fn: (provider: P) => H) {
const { providerRef } = useCommerce<P>()
const provider = providerRef.current
return fn(provider) // 여기서 provider는 provider.tsx에 있는 provider이다.
}
export function useSWRHook<H extends SWRHook<any>>(
hook: PickRequired<H, 'fetcher'>
) {
const fetcher = useFetcher()
return hook.useHook({
useData(ctx) {
const response = useData(hook, ctx?.input ?? [], fetcher, ctx?.swrOptions)
return response
},
})
}
...
use-cart.tsx
를 예로 보면, 위에서 호출한 hook.useHook
이 use-cart.tsx
에 정의된 handler.useHook
이 된다.
packages/vendure/src/cart/use-cart.tsx
를 보면, useHook 에서는 useData
를 호출한다.
// packages/vendure/src/cart/use-cart.tsx
import { SWRHook } from '@vercel/commerce/utils/types'
import useCart, { type UseCart } from '@vercel/commerce/cart/use-cart'
import { ActiveOrderQuery, CartFragment } from '../../schema'
import { normalizeCart } from '../utils/normalize'
import { useMemo } from 'react'
import { getCartQuery } from '../utils/queries/get-cart-query'
import type { GetCartHook } from '@vercel/commerce/types/cart'
...
export default useCart as UseCart<typeof handler>
export const handler: SWRHook<GetCartHook> = {
fetchOptions: {
query: getCartQuery,
},
async fetcher({ input: { cartId }, options, fetch }) {
const { activeOrder } = await fetch<ActiveOrderQuery>(options)
return activeOrder ? normalizeCart(activeOrder) : null
},
useHook:
({ useData }) =>
(input) => {
const response = useData({
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
})
return useMemo(
() =>
Object.create(response, {
isEmpty: {
get() {
return (response.data?.lineItems.length ?? 0) <= 0
},
enumerable: true,
},
}),
[response]
)
},
}
<nextjs-commerce>\packages\commerce\src\utils\use-data.tsx
를 보면, useSWR
이 보인다.
import useSWR, { SWRResponse } from 'swr'
...
const useData: UseData = (options, input, fetcherFn, swrOptions) => {
...
const fetcher = async (
url: string,
query?: string,
method?: string,
...args: any[]
) => {
try {
return await options.fetcher({
options: { url, query, method },
// Transform the input array into an object
input: args.reduce((obj, val, i) => {
obj[hookInput[i][0]!] = val
return obj
}, {}),
fetch: fetcherFn,
})
} catch (error) {
// SWR will not log errors, but any error that's not an instance
// of CommerceError is not welcomed by this hook
if (!(error instanceof CommerceError)) {
console.error(error)
}
throw error
}
}
const response = useSWR(
() => {
const opts = options.fetchOptions
return opts
? [opts.url, opts.query, opts.method, ...hookInput.map((e) => e[1])]
: null
},
fetcher,
swrOptions
)
...
return response as typeof response & { isLoading: boolean }
}
export default useData
댓글 없음:
댓글 쓰기