Replace use-cart with the new hook

This commit is contained in:
Luis Alvarez 2021-02-08 10:52:35 -05:00
parent 2c9b8b100d
commit aab2e7f7cc
8 changed files with 66 additions and 109 deletions

View File

@ -1,7 +1,10 @@
import { FC } from 'react'
import Link from 'next/link'
import cn from 'classnames'
import type { BigcommerceProvider } from '@framework'
import { LineItem } from '@framework/types'
import useCart from '@framework/cart/use-cart'
import useFake from '@commerce/cart/use-fake'
import useCustomer from '@framework/customer/use-customer'
import { Heart, Bag } from '@components/icons'
import { useUI } from '@components/ui/context'
@ -15,12 +18,14 @@ interface Props {
const countItem = (count: number, item: LineItem) => count + item.quantity
const UserNav: FC<Props> = ({ className, children }) => {
const UserNav: FC<Props> = ({ className }) => {
const { data } = useCart()
const { data: customer } = useCustomer()
const { toggleSidebar, closeSidebarIfPresent, openModal } = useUI()
const itemsCount = data?.lineItems.reduce(countItem, 0) ?? 0
const x = useFake<BigcommerceProvider>()
return (
<nav className={cn(s.root, className)}>
<div className={s.mainContainer}>

View File

@ -1,52 +1,4 @@
import type { HookFetcher } from '@commerce/utils/types'
import type { SwrOptions } from '@commerce/utils/use-data'
import useResponse from '@commerce/utils/use-response'
import useCommerceCart, { CartInput } from '@commerce/cart/use-cart'
import { normalizeCart } from '../lib/normalize'
import type { Cart, BigcommerceCart } from '../types'
import useCommerceCart, { UseCart } from '@commerce/cart/use-cart'
import { BigcommerceProvider } from '..'
const defaultOpts = {
url: '/api/bigcommerce/cart',
method: 'GET',
}
export const fetcher: HookFetcher<Cart | null, CartInput> = async (
options,
{ cartId },
fetch
) => {
const data = cartId
? await fetch<BigcommerceCart>({ ...defaultOpts, ...options })
: null
return data && normalizeCart(data)
}
export function extendHook(
customFetcher: typeof fetcher,
swrOptions?: SwrOptions<Cart | null, CartInput>
) {
const useCart = () => {
const response = useCommerceCart(defaultOpts, [], customFetcher, {
revalidateOnFocus: false,
...swrOptions,
})
const res = useResponse(response, {
descriptors: {
isEmpty: {
get() {
return (response.data?.lineItems.length ?? 0) <= 0
},
enumerable: true,
},
},
})
return res
}
useCart.extend = extendHook
return useCart
}
export default extendHook(fetcher)
export default useCommerceCart as UseCart<BigcommerceProvider>

View File

@ -49,14 +49,18 @@ const fetcher: Fetcher<any> = async ({
throw await getError(res)
}
const useCart: HookHandler<Cart, CartInput> = {
const useCart: HookHandler<Cart, CartInput, any, any, { isEmpty?: boolean }> = {
fetchOptions: {
url: '/api/bigcommerce/cart',
method: 'GET',
},
fetcher(context) {
return undefined as any
swrOptions: {
revalidateOnFocus: false,
},
// fetcher(context) {
// return undefined as any
// },
normalizer: normalizeCart,
onResponse(response) {
return Object.create(response, {
isEmpty: {

View File

@ -1,28 +0,0 @@
import Cookies from 'js-cookie'
import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types'
import useData, { ResponseState, SwrOptions } from '../utils/use-data'
import type { Cart } from '../types'
import { useCommerce } from '..'
export type CartResponse<Data> = ResponseState<Data> & { isEmpty?: boolean }
// Input expected by the `useCart` hook
export type CartInput = {
cartId?: Cart['id']
}
export default function useCart<Data extends Cart | null>(
options: HookFetcherOptions,
input: HookInput,
fetcherFn: HookFetcher<Data, CartInput>,
swrOptions?: SwrOptions<Data, CartInput>
): CartResponse<Data> {
const { providerRef, cartCookie } = useCommerce()
const fetcher: typeof fetcherFn = (options, input, fetch) => {
input.cartId = Cookies.get(cartCookie)
return fetcherFn(options, input, fetch)
}
const response = useData(options, input, fetcher, swrOptions)
return response
}

View File

@ -1,28 +1,46 @@
import { useMemo } from 'react'
import Cookies from 'js-cookie'
import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types'
import useData, { ResponseState, SwrOptions } from '../utils/use-data'
import type { Cart } from '../types'
import { useCommerce } from '..'
export type CartResponse<Data> = ResponseState<Data> & { isEmpty?: boolean }
import type { HookFetcherFn } from '../utils/types'
import useData from '../utils/use-data-2'
import { Provider, useCommerce } from '..'
// Input expected by the `useCart` hook
export type CartInput = {
cartId?: Cart['id']
}
export default function useCart<Data extends Cart | null>(
options: HookFetcherOptions,
input: HookInput,
fetcherFn: HookFetcher<Data, CartInput>,
swrOptions?: SwrOptions<Data, CartInput>
): CartResponse<Data> {
const { cartCookie } = useCommerce()
const fetcher: typeof fetcherFn = (options, input, fetch) => {
input.cartId = Cookies.get(cartCookie)
return fetcherFn(options, input, fetch)
}
const response = useData(options, input, fetcher, swrOptions)
export type CartResponse<P extends Provider> = ReturnType<
NonNullable<NonNullable<NonNullable<P['cart']>['useCart']>['onResponse']>
>
return response
export type UseCart<P extends Provider> = () => CartResponse<P>
export const fetcher: HookFetcherFn<Cart | null, CartInput> = async ({
options,
input: { cartId },
fetch,
normalize,
}) => {
const data = cartId ? await fetch({ ...options }) : null
return data && normalize ? normalize(data) : data
}
export default function useCart<P extends Provider>() {
const { providerRef, cartCookie } = useCommerce<P>()
const provider = providerRef.current
const opts = provider.cart?.useCart
const fetcherFn = opts?.fetcher ?? fetcher
const wrapper: typeof fetcher = (context) => {
context.input.cartId = Cookies.get(cartCookie)
return fetcherFn(context)
}
const response = useData(opts!, [], wrapper, opts?.swrOptions)
const memoizedResponse = useMemo(
() => (opts?.onResponse ? opts.onResponse(response) : response),
[response]
)
return memoizedResponse as CartResponse<P>
}

View File

@ -10,7 +10,11 @@ export type CartInput = {
cartId?: Cart['id']
}
const fetcher: HookFetcherFn<Cart | null, CartInput> = async ({
export type CartResponse<P extends Provider> = ReturnType<
NonNullable<NonNullable<NonNullable<P['cart']>['useCart']>['onResponse']>
>
export const fetcher: HookFetcherFn<Cart | null, CartInput> = async ({
options,
input: { cartId },
fetch,
@ -31,12 +35,11 @@ export default function useFake<P extends Provider>() {
context.input.cartId = Cookies.get(cartCookie)
return fetcherFn(context)
}
const response = useData(options, [], wrapper, opts?.swrOptions)
const memoizedResponse = useMemo(
() => (opts?.onResponse ? opts.onResponse(response) : response),
[response]
)
return memoizedResponse
return memoizedResponse as CartResponse<P>
}

View File

@ -21,9 +21,11 @@ export type Provider = CommerceConfig & {
cartNormalizer(data: any): Cart
}
export type HookHandler<Data, Input, Result = any, Body = any> = {
export type HookHandler<Data, Input, Result = any, Body = any, State = {}> = {
swrOptions?: SwrOptions<Data | null, Input, Result>
onResponse?(response: ResponseState<Data | null>): ResponseState<Data | null>
onResponse?(
response: ResponseState<Data | null>
): ResponseState<Data | null> & State
onMutation?: any
fetchOptions?: HookFetcherOptions
} & (

View File

@ -7,7 +7,7 @@ import type {
} from './types'
import defineProperty from './define-property'
import { CommerceError } from './errors'
import { useCommerce } from '..'
import { HookHandler, useCommerce } from '..'
export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface<
Data,
@ -20,7 +20,7 @@ export type ResponseState<Result> = responseInterface<Result, CommerceError> & {
}
export type UseData = <Data = any, Input = null, Result = any>(
options: HookFetcherOptions | (() => HookFetcherOptions | null),
options: HookHandler<Data, Input, Result>,
input: HookInput,
fetcherFn: HookFetcherFn<Data, Input, Result>,
swrOptions?: SwrOptions<Data, Input, Result>
@ -43,6 +43,7 @@ const useData: UseData = (options, input, fetcherFn, swrOptions) => {
return obj
}, {}),
fetch: fetcherRef.current,
normalize: options.normalizer,
})
} catch (error) {
// SWR will not log errors, but any error that's not an instance
@ -55,7 +56,7 @@ const useData: UseData = (options, input, fetcherFn, swrOptions) => {
}
const response = useSWR(
() => {
const opts = typeof options === 'function' ? options() : options
const opts = options.fetchOptions
return opts
? [opts.url, opts.query, opts.method, ...input.map((e) => e[1])]
: null