4
0
forked from crowetic/commerce

Added useResponse hook

This commit is contained in:
Luis Alvarez 2021-01-21 21:19:53 -05:00
parent 2613a5cec7
commit bafb8a4479
6 changed files with 57 additions and 24 deletions

View File

@ -1,10 +1,9 @@
import { normalizeCart } from '../lib/normalize' import { normalizeCart } from '../lib/normalize'
import type { HookFetcher } from '@commerce/utils/types' import type { HookFetcher } from '@commerce/utils/types'
import type { SwrOptions } from '@commerce/utils/use-data' import type { SwrOptions } from '@commerce/utils/use-data'
import defineProperty from '@commerce/utils/define-property' import useResponse from '@commerce/utils/use-response'
import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' import useCommerceCart, { CartInput } from '@commerce/cart/use-cart'
import type { Cart as BigCommerceCart } from '../api/cart' import type { Cart as BigCommerceCart } from '../api/cart'
import update from '@framework/lib/immutability'
const defaultOpts = { const defaultOpts = {
url: '/api/bigcommerce/cart', url: '/api/bigcommerce/cart',
@ -30,25 +29,21 @@ export function extendHook(
revalidateOnFocus: false, revalidateOnFocus: false,
...swrOptions, ...swrOptions,
}) })
const res = useResponse(response, {
// Uses a getter to only calculate the prop when required normalizer: normalizeCart,
// response.data is also a getter and it's better to not trigger it early descriptors: {
if (!('isEmpty' in response)) { isEmpty: {
defineProperty(response, 'isEmpty', { get() {
get() { return Object.values(response.data?.line_items ?? {}).every(
return Object.values(response.data?.line_items ?? {}).every( (items) => !items.length
(items) => !items.length )
) },
enumerable: true,
}, },
set: (x) => x, },
}) })
}
return response.data return res
? update(response, {
data: { $set: normalizeCart(response.data) },
})
: response
} }
useCart.extend = extendHook useCart.extend = extendHook

View File

@ -1,4 +1,3 @@
import type { responseInterface } from 'swr'
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types'
import useData, { ResponseState, SwrOptions } from '../utils/use-data' import useData, { ResponseState, SwrOptions } from '../utils/use-data'
@ -17,12 +16,10 @@ export default function useCart<Result>(
swrOptions?: SwrOptions<Result, CartInput> swrOptions?: SwrOptions<Result, CartInput>
): CartResponse<Result> { ): CartResponse<Result> {
const { cartCookie } = useCommerce() const { cartCookie } = useCommerce()
const fetcher: typeof fetcherFn = (options, input, fetch) => { const fetcher: typeof fetcherFn = (options, input, fetch) => {
input.cartId = Cookies.get(cartCookie) input.cartId = Cookies.get(cartCookie)
return fetcherFn(options, input, fetch) return fetcherFn(options, input, fetch)
} }
const response = useData(options, input, fetcher, swrOptions) const response = useData(options, input, fetcher, swrOptions)
return response return response

View File

@ -22,3 +22,5 @@ export type HookFetcherOptions = {
} }
export type HookInput = [string, string | number | boolean | undefined][] export type HookInput = [string, string | number | boolean | undefined][]
export type Override<T, K> = Omit<T, keyof K> & K

View File

@ -64,7 +64,7 @@ const useData: UseData = (options, input, fetcherFn, swrOptions) => {
get() { get() {
return response.data === undefined return response.data === undefined
}, },
set: (x) => x, enumerable: true,
}) })
} }

View File

@ -0,0 +1,40 @@
import { useMemo } from 'react'
import { responseInterface } from 'swr'
import { CommerceError } from './errors'
import { Override } from './types'
export type UseResponseOptions<
D,
R extends responseInterface<any, CommerceError>
> = {
descriptors?: PropertyDescriptorMap
normalizer?: (data: R['data']) => D
}
export type UseResponse = <D, R extends responseInterface<any, CommerceError>>(
response: R,
options: UseResponseOptions<D, R>
) => D extends object ? Override<R, { data?: D }> : R
const useResponse: UseResponse = (response, { descriptors, normalizer }) => {
const memoizedResponse = useMemo(
() =>
Object.create(response, {
...descriptors,
...(normalizer
? {
data: {
get() {
return normalizer(response.data)
},
enumerable: true,
},
}
: {}),
}),
[response]
)
return memoizedResponse
}
export default useResponse

View File

@ -1,4 +1,3 @@
import type { responseInterface } from 'swr'
import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types'
import useData, { ResponseState, SwrOptions } from '../utils/use-data' import useData, { ResponseState, SwrOptions } from '../utils/use-data'