[컴][웹] nextjs commerce 에서 SWR handler.useHook 이 호출되는 과정

nextjs commerce 에서 SWR handler.useHook 이 호출되는 과정

  1. packages/commerce/src/cart/use-cart.tsx, useCart 에서 useSWRHook 호출
  2. packages/commerce/src/utils/use-hook.ts, useSWRHook 에서 hook.useHook 호출
  3. packages/vendure/src/cart/use-cart.tsx, handler.useHook

자세한 code

packages/commerce/src/cart/use-cart.tsxuseCart를 보면, useSWRHook 를 호출하고 있다. 이 때 hook을 넘기는데, 이 hook 은 useHook을 이용해서 얻는다. 아래 packages/commerce/src/utils/use-hook.ts 를 보면 된다.

useHook은 parameter 로 fn 을 넘기는데, 이 fn 에서 어떤 hook 을 사용할 것인지 고른다고 보면 된다. 참고로, 이 useHookhook.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.useHookuse-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

swr package

See Also

  1. 쿠…sal: [컴][웹] next.js commerce + vendure 서버 실행

댓글 없음:

댓글 쓰기