mirror of
https://github.com/vercel/commerce.git
synced 2025-03-14 22:42:33 +00:00
Merge branch 'agnostic' of https://github.com/vercel/commerce into agnostic
This commit is contained in:
commit
af201cccdc
@ -52,6 +52,10 @@ type Action =
|
||||
type: 'SET_MODAL_VIEW'
|
||||
view: MODAL_VIEWS
|
||||
}
|
||||
| {
|
||||
type: 'SET_USER_AVATAR'
|
||||
value: string
|
||||
}
|
||||
|
||||
type MODAL_VIEWS = 'SIGNUP_VIEW' | 'LOGIN_VIEW' | 'FORGOT_VIEW'
|
||||
type ToastText = string
|
||||
@ -147,6 +151,9 @@ export const UIProvider: FC = (props) => {
|
||||
const openToast = () => dispatch({ type: 'OPEN_TOAST' })
|
||||
const closeToast = () => dispatch({ type: 'CLOSE_TOAST' })
|
||||
|
||||
const setUserAvatar = (value: string) =>
|
||||
dispatch({ type: 'SET_USER_AVATAR', value })
|
||||
|
||||
const setModalView = (view: MODAL_VIEWS) =>
|
||||
dispatch({ type: 'SET_MODAL_VIEW', view })
|
||||
|
||||
@ -164,6 +171,7 @@ export const UIProvider: FC = (props) => {
|
||||
setModalView,
|
||||
openToast,
|
||||
closeToast,
|
||||
setUserAvatar,
|
||||
}),
|
||||
[state]
|
||||
)
|
||||
|
@ -1,38 +1,4 @@
|
||||
import type { HookFetcher } from '@commerce/utils/types'
|
||||
import type { SwrOptions } from '@commerce/utils/use-data'
|
||||
import useCommerceCustomer from '@commerce/use-customer'
|
||||
import type { Customer, CustomerData } from '../api/customers'
|
||||
import useCustomer, { UseCustomer } from '@commerce/customer/use-customer'
|
||||
import type { BigcommerceProvider } from '..'
|
||||
|
||||
const defaultOpts = {
|
||||
url: '/api/bigcommerce/customers',
|
||||
method: 'GET',
|
||||
}
|
||||
|
||||
export type { Customer }
|
||||
|
||||
export const fetcher: HookFetcher<Customer | null> = async (
|
||||
options,
|
||||
_,
|
||||
fetch
|
||||
) => {
|
||||
const data = await fetch<CustomerData | null>({ ...defaultOpts, ...options })
|
||||
return data?.customer ?? null
|
||||
}
|
||||
|
||||
export function extendHook(
|
||||
customFetcher: typeof fetcher,
|
||||
swrOptions?: SwrOptions<Customer | null>
|
||||
) {
|
||||
const useCustomer = () => {
|
||||
return useCommerceCustomer(defaultOpts, [], customFetcher, {
|
||||
revalidateOnFocus: false,
|
||||
...swrOptions,
|
||||
})
|
||||
}
|
||||
|
||||
useCustomer.extend = extendHook
|
||||
|
||||
return useCustomer
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
||||
export default useCustomer as UseCustomer<BigcommerceProvider>
|
||||
|
@ -1,63 +1,4 @@
|
||||
import type { HookFetcher } from '@commerce/utils/types'
|
||||
import type { SwrOptions } from '@commerce/utils/use-data'
|
||||
import useCommerceSearch from '@commerce/products/use-search'
|
||||
import type { SearchProductsData } from '../api/catalog/products'
|
||||
import useSearch, { UseSearch } from '@commerce/products/use-search'
|
||||
import type { BigcommerceProvider } from '..'
|
||||
|
||||
const defaultOpts = {
|
||||
url: '/api/bigcommerce/catalog/products',
|
||||
method: 'GET',
|
||||
}
|
||||
|
||||
export type SearchProductsInput = {
|
||||
search?: string
|
||||
categoryId?: number
|
||||
brandId?: number
|
||||
sort?: string
|
||||
}
|
||||
|
||||
export const fetcher: HookFetcher<SearchProductsData, SearchProductsInput> = (
|
||||
options,
|
||||
{ search, categoryId, brandId, sort },
|
||||
fetch
|
||||
) => {
|
||||
// Use a dummy base as we only care about the relative path
|
||||
const url = new URL(options?.url ?? defaultOpts.url, 'http://a')
|
||||
|
||||
if (search) url.searchParams.set('search', search)
|
||||
if (Number.isInteger(categoryId))
|
||||
url.searchParams.set('category', String(categoryId))
|
||||
if (Number.isInteger(brandId)) url.searchParams.set('brand', String(brandId))
|
||||
if (sort) url.searchParams.set('sort', sort)
|
||||
|
||||
return fetch({
|
||||
url: url.pathname + url.search,
|
||||
method: options?.method ?? defaultOpts.method,
|
||||
})
|
||||
}
|
||||
|
||||
export function extendHook(
|
||||
customFetcher: typeof fetcher,
|
||||
swrOptions?: SwrOptions<SearchProductsData, SearchProductsInput>
|
||||
) {
|
||||
const useSearch = (input: SearchProductsInput = {}) => {
|
||||
const response = useCommerceSearch(
|
||||
defaultOpts,
|
||||
[
|
||||
['search', input.search],
|
||||
['categoryId', input.categoryId],
|
||||
['brandId', input.brandId],
|
||||
['sort', input.sort],
|
||||
],
|
||||
customFetcher,
|
||||
{ revalidateOnFocus: false, ...swrOptions }
|
||||
)
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
useSearch.extend = extendHook
|
||||
|
||||
return useSearch
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
||||
export default useSearch as UseSearch<BigcommerceProvider>
|
||||
|
@ -4,6 +4,8 @@ import type { Fetcher, HookHandler } from '@commerce/utils/types'
|
||||
import type { FetchCartInput } from '@commerce/cart/use-cart'
|
||||
import { normalizeCart } from './lib/normalize'
|
||||
import type { Wishlist } from './api/wishlist'
|
||||
import type { Customer, CustomerData } from './api/customers'
|
||||
import type { SearchProductsData } from './api/catalog/products'
|
||||
import useCustomer from './customer/use-customer'
|
||||
import type { Cart } from './types'
|
||||
|
||||
@ -48,15 +50,16 @@ const useCart: HookHandler<
|
||||
Cart | null,
|
||||
{},
|
||||
FetchCartInput,
|
||||
any,
|
||||
any,
|
||||
{ isEmpty?: boolean }
|
||||
> = {
|
||||
fetchOptions: {
|
||||
url: '/api/bigcommerce/cart',
|
||||
method: 'GET',
|
||||
},
|
||||
normalizer: normalizeCart,
|
||||
async fetcher({ input: { cartId }, options, fetch }) {
|
||||
const data = cartId ? await fetch(options) : null
|
||||
return data && normalizeCart(data)
|
||||
},
|
||||
useHook({ input, useData }) {
|
||||
const response = useData({
|
||||
swrOptions: { revalidateOnFocus: false, ...input.swrOptions },
|
||||
@ -81,8 +84,6 @@ const useWishlist: HookHandler<
|
||||
Wishlist | null,
|
||||
{ includeProducts?: boolean },
|
||||
{ customerId?: number; includeProducts: boolean },
|
||||
any,
|
||||
any,
|
||||
{ isEmpty?: boolean }
|
||||
> = {
|
||||
fetchOptions: {
|
||||
@ -130,6 +131,73 @@ const useWishlist: HookHandler<
|
||||
},
|
||||
}
|
||||
|
||||
const useCustomerHandler: HookHandler<Customer | null> = {
|
||||
fetchOptions: {
|
||||
url: '/api/bigcommerce/customers',
|
||||
method: 'GET',
|
||||
},
|
||||
async fetcher({ options, fetch }) {
|
||||
const data = await fetch<CustomerData | null>(options)
|
||||
return data?.customer ?? null
|
||||
},
|
||||
useHook({ input, useData }) {
|
||||
return useData({
|
||||
swrOptions: {
|
||||
revalidateOnFocus: false,
|
||||
...input.swrOptions,
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
export type SearchProductsInput = {
|
||||
search?: string
|
||||
categoryId?: number
|
||||
brandId?: number
|
||||
sort?: string
|
||||
}
|
||||
|
||||
const useSearch: HookHandler<
|
||||
SearchProductsData,
|
||||
SearchProductsInput,
|
||||
SearchProductsInput
|
||||
> = {
|
||||
fetchOptions: {
|
||||
url: '/api/bigcommerce/catalog/products',
|
||||
method: 'GET',
|
||||
},
|
||||
fetcher({ input: { search, categoryId, brandId, sort }, options, fetch }) {
|
||||
// Use a dummy base as we only care about the relative path
|
||||
const url = new URL(options.url!, 'http://a')
|
||||
|
||||
if (search) url.searchParams.set('search', search)
|
||||
if (Number.isInteger(categoryId))
|
||||
url.searchParams.set('category', String(categoryId))
|
||||
if (Number.isInteger(brandId))
|
||||
url.searchParams.set('brand', String(brandId))
|
||||
if (sort) url.searchParams.set('sort', sort)
|
||||
|
||||
return fetch({
|
||||
url: url.pathname + url.search,
|
||||
method: options.method,
|
||||
})
|
||||
},
|
||||
useHook({ input, useData }) {
|
||||
return useData({
|
||||
input: [
|
||||
['search', input.search],
|
||||
['categoryId', input.categoryId],
|
||||
['brandId', input.brandId],
|
||||
['sort', input.sort],
|
||||
],
|
||||
swrOptions: {
|
||||
revalidateOnFocus: false,
|
||||
...input.swrOptions,
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
export const bigcommerceProvider = {
|
||||
locale: 'en-us',
|
||||
cartCookie: 'bc_cartId',
|
||||
@ -137,6 +205,8 @@ export const bigcommerceProvider = {
|
||||
cartNormalizer: normalizeCart,
|
||||
cart: { useCart },
|
||||
wishlist: { useWishlist },
|
||||
customer: { useCustomer: useCustomerHandler },
|
||||
products: { useSearch },
|
||||
}
|
||||
|
||||
export type BigcommerceProvider = typeof bigcommerceProvider
|
||||
|
@ -6,7 +6,7 @@ import type {
|
||||
UseHookInput,
|
||||
UseHookResponse,
|
||||
} from '../utils/types'
|
||||
import useData from '../utils/use-data-2'
|
||||
import useData from '../utils/use-data'
|
||||
import { Provider, useCommerce } from '..'
|
||||
|
||||
export type FetchCartInput = {
|
||||
@ -34,10 +34,8 @@ export const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({
|
||||
options,
|
||||
input: { cartId },
|
||||
fetch,
|
||||
normalize,
|
||||
}) => {
|
||||
const data = cartId ? await fetch({ ...options }) : null
|
||||
return data && normalize ? normalize(data) : data
|
||||
return cartId ? await fetch({ ...options }) : null
|
||||
}
|
||||
|
||||
export default function useCart<P extends Provider>(
|
||||
|
56
framework/commerce/customer/use-customer.tsx
Normal file
56
framework/commerce/customer/use-customer.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import type { Customer } from '../types'
|
||||
import type {
|
||||
Prop,
|
||||
HookFetcherFn,
|
||||
UseHookInput,
|
||||
UseHookResponse,
|
||||
} from '../utils/types'
|
||||
import defaultFetcher from '../utils/default-fetcher'
|
||||
import useData from '../utils/use-data'
|
||||
import { Provider, useCommerce } from '..'
|
||||
|
||||
export type UseCustomerHandler<P extends Provider> = Prop<
|
||||
Prop<P, 'customer'>,
|
||||
'useCustomer'
|
||||
>
|
||||
|
||||
export type UseCustomerInput<P extends Provider> = UseHookInput<
|
||||
UseCustomerHandler<P>
|
||||
>
|
||||
|
||||
export type CustomerResponse<P extends Provider> = UseHookResponse<
|
||||
UseCustomerHandler<P>
|
||||
>
|
||||
|
||||
export type UseCustomer<P extends Provider> = Partial<
|
||||
UseCustomerInput<P>
|
||||
> extends UseCustomerInput<P>
|
||||
? (input?: UseCustomerInput<P>) => CustomerResponse<P>
|
||||
: (input: UseCustomerInput<P>) => CustomerResponse<P>
|
||||
|
||||
export const fetcher = defaultFetcher as HookFetcherFn<Customer | null>
|
||||
|
||||
export default function useCustomer<P extends Provider>(
|
||||
input: UseCustomerInput<P> = {}
|
||||
) {
|
||||
const { providerRef, fetcherRef } = useCommerce<P>()
|
||||
|
||||
const provider = providerRef.current
|
||||
const opts = provider.customer?.useCustomer
|
||||
|
||||
const fetcherFn = opts?.fetcher ?? fetcher
|
||||
const useHook = opts?.useHook ?? ((ctx) => ctx.useData())
|
||||
|
||||
return useHook({
|
||||
input,
|
||||
useData(ctx) {
|
||||
const response = useData(
|
||||
{ ...opts!, fetcher: fetcherFn },
|
||||
ctx?.input ?? [],
|
||||
provider.fetcher ?? fetcherRef.current,
|
||||
ctx?.swrOptions ?? input.swrOptions
|
||||
)
|
||||
return response
|
||||
},
|
||||
})
|
||||
}
|
@ -6,10 +6,9 @@ import {
|
||||
useMemo,
|
||||
useRef,
|
||||
} from 'react'
|
||||
import * as React from 'react'
|
||||
import { Fetcher, HookHandler } from './utils/types'
|
||||
import type { FetchCartInput } from './cart/use-cart'
|
||||
import type { Cart, Wishlist } from './types'
|
||||
import type { Cart, Wishlist, Customer, SearchProductsData } from './types'
|
||||
|
||||
const Commerce = createContext<CommerceContextValue<any> | {}>({})
|
||||
|
||||
@ -21,6 +20,12 @@ export type Provider = CommerceConfig & {
|
||||
wishlist?: {
|
||||
useWishlist?: HookHandler<Wishlist | null, any, any>
|
||||
}
|
||||
customer: {
|
||||
useCustomer?: HookHandler<Customer | null, any, any>
|
||||
}
|
||||
products: {
|
||||
useSearch?: HookHandler<SearchProductsData, any, any>
|
||||
}
|
||||
}
|
||||
|
||||
export type CommerceProps<P extends Provider> = {
|
||||
|
@ -1,5 +1,57 @@
|
||||
import type { SearchProductsData } from '../types'
|
||||
import type {
|
||||
Prop,
|
||||
HookFetcherFn,
|
||||
UseHookInput,
|
||||
UseHookResponse,
|
||||
} from '../utils/types'
|
||||
import defaultFetcher from '../utils/default-fetcher'
|
||||
import useData from '../utils/use-data'
|
||||
import { Provider, useCommerce } from '..'
|
||||
import { BigcommerceProvider } from '@framework'
|
||||
|
||||
const useSearch = useData
|
||||
export type UseSearchHandler<P extends Provider> = Prop<
|
||||
Prop<P, 'products'>,
|
||||
'useSearch'
|
||||
>
|
||||
|
||||
export default useSearch
|
||||
export type UseSeachInput<P extends Provider> = UseHookInput<
|
||||
UseSearchHandler<P>
|
||||
>
|
||||
|
||||
export type SearchResponse<P extends Provider> = UseHookResponse<
|
||||
UseSearchHandler<P>
|
||||
>
|
||||
|
||||
export type UseSearch<P extends Provider> = Partial<
|
||||
UseSeachInput<P>
|
||||
> extends UseSeachInput<P>
|
||||
? (input?: UseSeachInput<P>) => SearchResponse<P>
|
||||
: (input: UseSeachInput<P>) => SearchResponse<P>
|
||||
|
||||
export const fetcher = defaultFetcher as HookFetcherFn<SearchProductsData>
|
||||
|
||||
export default function useSearch<P extends Provider>(
|
||||
input: UseSeachInput<P> = {}
|
||||
) {
|
||||
const { providerRef, fetcherRef } = useCommerce<P>()
|
||||
|
||||
const provider = providerRef.current
|
||||
const opts = provider.products?.useSearch
|
||||
|
||||
const fetcherFn = opts?.fetcher ?? fetcher
|
||||
const useHook = opts?.useHook ?? ((ctx) => ctx.useData())
|
||||
|
||||
return useHook({
|
||||
input,
|
||||
useData(ctx) {
|
||||
const response = useData(
|
||||
{ ...opts!, fetcher: fetcherFn },
|
||||
ctx?.input ?? [],
|
||||
provider.fetcher ?? fetcherRef.current,
|
||||
ctx?.swrOptions ?? input.swrOptions
|
||||
)
|
||||
return response
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
import type { Wishlist as BCWishlist } from '@framework/api/wishlist'
|
||||
import type { Customer as BCCustomer } from '@framework/api/customers'
|
||||
import type { SearchProductsData as BCSearchProductsData } from '@framework/api/catalog/products'
|
||||
|
||||
export interface Discount {
|
||||
// The value of the discount, can be an amount or percentage
|
||||
@ -92,6 +94,12 @@ export interface Cart {
|
||||
// TODO: Properly define this type
|
||||
export interface Wishlist extends BCWishlist {}
|
||||
|
||||
// TODO: Properly define this type
|
||||
export interface Customer extends BCCustomer {}
|
||||
|
||||
// TODO: Properly define this type
|
||||
export interface SearchProductsData extends BCSearchProductsData {}
|
||||
|
||||
/**
|
||||
* Cart mutations
|
||||
*/
|
||||
|
@ -1,5 +0,0 @@
|
||||
import useData from './utils/use-data'
|
||||
|
||||
const useCustomer = useData
|
||||
|
||||
export default useCustomer
|
6
framework/commerce/utils/default-fetcher.ts
Normal file
6
framework/commerce/utils/default-fetcher.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import type { HookFetcherFn } from './types'
|
||||
|
||||
const defaultFetcher: HookFetcherFn<any> = ({ options, fetch }) =>
|
||||
fetch(options)
|
||||
|
||||
export default defaultFetcher
|
@ -40,7 +40,6 @@ export type HookFetcherFn<
|
||||
options: HookFetcherOptions
|
||||
input: Input
|
||||
fetch: <T = Result, B = Body>(options: FetcherOptions<B>) => Promise<T>
|
||||
normalize?(data: Result): Data
|
||||
}) => Data | Promise<Data>
|
||||
|
||||
export type HookFetcherOptions = { method?: string } & (
|
||||
@ -63,23 +62,18 @@ export type HookHandler<
|
||||
Input extends { [k: string]: unknown } = {},
|
||||
// Input expected before doing a fetch operation
|
||||
FetchInput extends HookFetchInput = {},
|
||||
// Data returned by the API after a fetch operation
|
||||
Result = any,
|
||||
// Body expected by the API endpoint
|
||||
Body = any,
|
||||
// Custom state added to the response object of SWR
|
||||
State = {}
|
||||
> = {
|
||||
useHook?(context: {
|
||||
input: Input & { swrOptions?: SwrOptions<Data, FetchInput, Result> }
|
||||
input: Input & { swrOptions?: SwrOptions<Data, FetchInput> }
|
||||
useData(context?: {
|
||||
input?: HookFetchInput | HookSwrInput
|
||||
swrOptions?: SwrOptions<Data, FetchInput, Result>
|
||||
swrOptions?: SwrOptions<Data, FetchInput>
|
||||
}): ResponseState<Data>
|
||||
}): ResponseState<Data> & State
|
||||
fetchOptions: HookFetcherOptions
|
||||
fetcher?: HookFetcherFn<Data, FetchInput, Result, Body>
|
||||
normalizer?(data: Result): Data
|
||||
fetcher?: HookFetcherFn<Data, FetchInput>
|
||||
}
|
||||
|
||||
export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface<
|
||||
|
@ -1,84 +0,0 @@
|
||||
import useSWR, { responseInterface } from 'swr'
|
||||
import type {
|
||||
HookHandler,
|
||||
HookSwrInput,
|
||||
HookFetchInput,
|
||||
PickRequired,
|
||||
Fetcher,
|
||||
SwrOptions,
|
||||
} from './types'
|
||||
import defineProperty from './define-property'
|
||||
import { CommerceError } from './errors'
|
||||
|
||||
export type ResponseState<Result> = responseInterface<Result, CommerceError> & {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export type UseData = <
|
||||
Data = any,
|
||||
Input extends { [k: string]: unknown } = {},
|
||||
FetchInput extends HookFetchInput = {},
|
||||
Result = any,
|
||||
Body = any
|
||||
>(
|
||||
options: PickRequired<
|
||||
HookHandler<Data, Input, FetchInput, Result, Body>,
|
||||
'fetcher'
|
||||
>,
|
||||
input: HookFetchInput | HookSwrInput,
|
||||
fetcherFn: Fetcher,
|
||||
swrOptions?: SwrOptions<Data, FetchInput, Result>
|
||||
) => ResponseState<Data>
|
||||
|
||||
const useData: UseData = (options, input, fetcherFn, swrOptions) => {
|
||||
const hookInput = Array.isArray(input) ? input : Object.entries(input)
|
||||
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,
|
||||
normalize: options.normalizer,
|
||||
})
|
||||
} 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
|
||||
)
|
||||
|
||||
if (!('isLoading' in response)) {
|
||||
defineProperty(response, 'isLoading', {
|
||||
get() {
|
||||
return response.data === undefined
|
||||
},
|
||||
enumerable: true,
|
||||
})
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
export default useData
|
@ -1,44 +1,48 @@
|
||||
import useSWR, { ConfigInterface, responseInterface } from 'swr'
|
||||
import type { HookSwrInput, HookFetcher, HookFetcherOptions } from './types'
|
||||
import useSWR, { responseInterface } from 'swr'
|
||||
import type {
|
||||
HookHandler,
|
||||
HookSwrInput,
|
||||
HookFetchInput,
|
||||
PickRequired,
|
||||
Fetcher,
|
||||
SwrOptions,
|
||||
} from './types'
|
||||
import defineProperty from './define-property'
|
||||
import { CommerceError } from './errors'
|
||||
import { useCommerce } from '..'
|
||||
|
||||
export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface<
|
||||
Data,
|
||||
CommerceError,
|
||||
HookFetcher<Data, Input, Result>
|
||||
>
|
||||
|
||||
export type ResponseState<Result> = responseInterface<Result, CommerceError> & {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export type UseData = <Data = any, Input = null, Result = any>(
|
||||
options: HookFetcherOptions | (() => HookFetcherOptions | null),
|
||||
input: HookSwrInput,
|
||||
fetcherFn: HookFetcher<Data, Input, Result>,
|
||||
swrOptions?: SwrOptions<Data, Input, Result>
|
||||
export type UseData = <
|
||||
Data = any,
|
||||
Input extends { [k: string]: unknown } = {},
|
||||
FetchInput extends HookFetchInput = {}
|
||||
>(
|
||||
options: PickRequired<HookHandler<Data, Input, FetchInput>, 'fetcher'>,
|
||||
input: HookFetchInput | HookSwrInput,
|
||||
fetcherFn: Fetcher,
|
||||
swrOptions?: SwrOptions<Data, FetchInput>
|
||||
) => ResponseState<Data>
|
||||
|
||||
const useData: UseData = (options, input, fetcherFn, swrOptions) => {
|
||||
const { fetcherRef } = useCommerce()
|
||||
const hookInput = Array.isArray(input) ? input : Object.entries(input)
|
||||
const fetcher = async (
|
||||
url?: string,
|
||||
url: string,
|
||||
query?: string,
|
||||
method?: string,
|
||||
...args: any[]
|
||||
) => {
|
||||
try {
|
||||
return await fetcherFn(
|
||||
{ url, query, method },
|
||||
return await options.fetcher({
|
||||
options: { url, query, method },
|
||||
// Transform the input array into an object
|
||||
args.reduce((obj, val, i) => {
|
||||
obj[input[i][0]!] = val
|
||||
input: args.reduce((obj, val, i) => {
|
||||
obj[hookInput[i][0]!] = val
|
||||
return obj
|
||||
}, {}),
|
||||
fetcherRef.current
|
||||
)
|
||||
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
|
||||
@ -50,9 +54,9 @@ 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])]
|
||||
? [opts.url, opts.query, opts.method, ...hookInput.map((e) => e[1])]
|
||||
: null
|
||||
},
|
||||
fetcher,
|
||||
|
@ -5,7 +5,8 @@ import type {
|
||||
UseHookInput,
|
||||
UseHookResponse,
|
||||
} from '../utils/types'
|
||||
import useData from '../utils/use-data-2'
|
||||
import defaultFetcher from '../utils/default-fetcher'
|
||||
import useData from '../utils/use-data'
|
||||
import { Provider, useCommerce } from '..'
|
||||
|
||||
export type UseWishlistHandler<P extends Provider> = Prop<
|
||||
@ -22,19 +23,12 @@ export type WishlistResponse<P extends Provider> = UseHookResponse<
|
||||
>
|
||||
|
||||
export type UseWishlist<P extends Provider> = Partial<
|
||||
WishlistResponse<P>
|
||||
> extends WishlistResponse<P>
|
||||
? (input?: WishlistResponse<P>) => WishlistResponse<P>
|
||||
: (input: WishlistResponse<P>) => WishlistResponse<P>
|
||||
UseWishlistInput<P>
|
||||
> extends UseWishlistInput<P>
|
||||
? (input?: UseWishlistInput<P>) => WishlistResponse<P>
|
||||
: (input: UseWishlistInput<P>) => WishlistResponse<P>
|
||||
|
||||
export const fetcher: HookFetcherFn<Wishlist | null> = async ({
|
||||
options,
|
||||
fetch,
|
||||
normalize,
|
||||
}) => {
|
||||
const data = await fetch({ ...options })
|
||||
return data && normalize ? normalize(data) : data
|
||||
}
|
||||
export const fetcher = defaultFetcher as HookFetcherFn<Wishlist | null>
|
||||
|
||||
export default function useWishlist<P extends Provider>(
|
||||
input: UseWishlistInput<P> = {}
|
||||
|
Loading…
x
Reference in New Issue
Block a user