Moved more hooks and updated types to make them smaller

This commit is contained in:
Luis Alvarez 2021-02-18 23:22:45 -05:00
parent a7df259bda
commit 2e1d2610bb
11 changed files with 128 additions and 112 deletions

View File

@ -1,13 +1,12 @@
import { useMemo } from 'react'
import { HookHandler } from '@commerce/utils/types'
import { SWRHook } from '@commerce/utils/types'
import useCart, { UseCart, FetchCartInput } from '@commerce/cart/use-cart'
import { normalizeCart } from '../lib/normalize'
import type { Cart } from '../types'
import type { BigcommerceProvider } from '..'
export default useCart as UseCart<BigcommerceProvider>
export default useCart as UseCart<typeof handler>
export const handler: HookHandler<
export const handler: SWRHook<
Cart | null,
{},
FetchCartInput,
@ -21,9 +20,9 @@ export const handler: HookHandler<
const data = cartId ? await fetch(options) : null
return data && normalizeCart(data)
},
useHook({ input, useData }) {
useHook: ({ useData }) => (input) => {
const response = useData({
swrOptions: { revalidateOnFocus: false, ...input.swrOptions },
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
})
return useMemo(

View File

@ -46,7 +46,7 @@ export const handler = {
ctx: { item?: T } = {}
) => {
const { item } = ctx
const { mutate } = useCart() as any
const { mutate } = useCart()
const removeItem: RemoveItemFn<LineItem> = async (input) => {
const itemId = input?.id ?? item?.id

View File

@ -1,4 +1,5 @@
import useHook, { useHookHandler } from '../utils/use-hook'
import { useHook, useMutationHook } from '../utils/use-hook'
import { mutationFetcher } from '../utils/default-fetcher'
import type { MutationHook, HookFetcherFn } from '../utils/types'
import type { Cart, CartItemBody, AddCartItemBody } from '../types'
import type { Provider } from '..'
@ -6,9 +7,7 @@ import type { Provider } from '..'
export const fetcher: HookFetcherFn<
Cart,
AddCartItemBody<CartItemBody>
> = async ({ options, input, fetch }) => {
return fetch({ ...options, body: input })
}
> = mutationFetcher
export type UseAddItem<
H extends MutationHook<any, any, any> = MutationHook<Cart, {}, CartItemBody>
@ -17,8 +16,8 @@ export type UseAddItem<
const fn = (provider: Provider) => provider.cart?.useAddItem!
const useAddItem: UseAddItem = (...args) => {
const handler = useHookHandler(fn, fetcher)
return handler(useHook(fn, fetcher))(...args)
const hook = useHook(fn)
return useMutationHook({ fetcher, ...hook })(...args)
}
export default useAddItem

View File

@ -1,34 +1,21 @@
import Cookies from 'js-cookie'
import type { Cart } from '../types'
import type {
Prop,
HookFetcherFn,
UseHookInput,
UseHookResponse,
} from '../utils/types'
import useData from '../utils/use-data'
import type { HookFetcherFn, SWRHook } from '../utils/types'
import { Provider, useCommerce } from '..'
import { useHook, useSWRHook } from '@commerce/utils/use-hook'
export type FetchCartInput = {
cartId?: Cart['id']
}
export type UseCartHandler<P extends Provider> = Prop<
Prop<P, 'cart'>,
'useCart'
>
export type UseCartInput<P extends Provider> = UseHookInput<UseCartHandler<P>>
export type CartResponse<P extends Provider> = UseHookResponse<
UseCartHandler<P>
>
export type UseCart<P extends Provider> = Partial<
UseCartInput<P>
> extends UseCartInput<P>
? (input?: UseCartInput<P>) => CartResponse<P>
: (input: UseCartInput<P>) => CartResponse<P>
export type UseCart<
H extends SWRHook<any, any, any> = SWRHook<
Cart | null,
{},
FetchCartInput,
{ isEmpty?: boolean }
>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({
options,
@ -38,32 +25,17 @@ export const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({
return cartId ? await fetch({ ...options }) : null
}
export default function useCart<P extends Provider>(
input: UseCartInput<P> = {}
) {
const { providerRef, fetcherRef, cartCookie } = useCommerce<P>()
const provider = providerRef.current
const opts = provider.cart?.useCart
const fetcherFn = opts?.fetcher ?? fetcher
const useHook = opts?.useHook ?? ((ctx) => ctx.useData())
const fn = (provider: Provider) => provider.cart?.useCart!
const useCart: UseCart = (input) => {
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 useHook({
input,
useData(ctx) {
const response = useData(
{ ...opts!, fetcher: wrapper },
ctx?.input ?? [],
provider.fetcher ?? fetcherRef.current,
ctx?.swrOptions ?? input.swrOptions
)
return response
},
})
return useSWRHook({ ...hook, fetcher: wrapper })(input)
}
export default useCart

View File

@ -1,6 +1,6 @@
import useHook, { useHookHandler } from '../utils/use-hook'
import { useHook, useMutationHook } from '../utils/use-hook'
import { mutationFetcher } from '../utils/default-fetcher'
import type { MutationHook } from '../utils/types'
import type { HookFetcherFn, MutationHook } from '../utils/types'
import type { Cart, LineItem, RemoveCartItemBody } from '../types'
import type { Provider } from '..'
@ -20,13 +20,16 @@ export type UseRemoveItem<
>
> = ReturnType<H['useHook']>
export const fetcher = mutationFetcher
export const fetcher: HookFetcherFn<
Cart | null,
RemoveCartItemBody
> = mutationFetcher
const fn = (provider: Provider) => provider.cart?.useRemoveItem!
const useRemoveItem: UseRemoveItem = (input) => {
const handler = useHookHandler(fn, fetcher)
return handler(useHook(fn, fetcher))(input)
const hook = useHook(fn)
return useMutationHook({ fetcher, ...hook })(input)
}
export default useRemoveItem

View File

@ -1,6 +1,6 @@
import useHook, { useHookHandler } from '../utils/use-hook'
import { useHook, useMutationHook } from '../utils/use-hook'
import { mutationFetcher } from '../utils/default-fetcher'
import type { MutationHook } from '../utils/types'
import type { HookFetcherFn, MutationHook } from '../utils/types'
import type { Cart, CartItemBody, LineItem, UpdateCartItemBody } from '../types'
import type { Provider } from '..'
@ -23,13 +23,16 @@ export type UseUpdateItem<
>
> = ReturnType<H['useHook']>
export const fetcher = mutationFetcher
export const fetcher: HookFetcherFn<
Cart | null,
UpdateCartItemBody<CartItemBody>
> = mutationFetcher
const fn = (provider: Provider) => provider.cart?.useUpdateItem!
const useUpdateItem: UseUpdateItem = (input) => {
const handler = useHookHandler(fn, fetcher)
return handler(useHook(fn, fetcher))(input)
const hook = useHook(fn)
return useMutationHook({ fetcher, ...hook })(input)
}
export default useUpdateItem

View File

@ -6,7 +6,7 @@ import {
useMemo,
useRef,
} from 'react'
import { Fetcher, HookHandler, MutationHook } from './utils/types'
import { Fetcher, HookHandler, SWRHook, MutationHook } from './utils/types'
import type { FetchCartInput } from './cart/use-cart'
import type { Cart, Wishlist, Customer, SearchProductsData } from './types'
@ -15,7 +15,7 @@ const Commerce = createContext<CommerceContextValue<any> | {}>({})
export type Provider = CommerceConfig & {
fetcher: Fetcher
cart?: {
useCart?: HookHandler<Cart | null, any, FetchCartInput>
useCart?: SWRHook<Cart | null, any, FetchCartInput>
useAddItem?: MutationHook<any, any, any>
useUpdateItem?: MutationHook<any, any, any>
useRemoveItem?: MutationHook<any, any, any>

View File

@ -3,7 +3,7 @@ import type { HookFetcherFn } from './types'
const defaultFetcher: HookFetcherFn<any> = ({ options, fetch }) =>
fetch(options)
export const mutationFetcher: HookFetcherFn<any> = ({
export const mutationFetcher: HookFetcherFn<any, any> = ({
input,
options,
fetch,

View File

@ -9,7 +9,9 @@ export type Override<T, K> = Omit<T, keyof K> & K
* Returns the properties in T with the properties in type K changed from optional to required
*/
export type PickRequired<T, K extends keyof T> = Omit<T, K> &
Required<Pick<T, K>>
{
[P in K]-?: NonNullable<T[P]>
}
/**
* Core fetcher added by CommerceProvider
@ -83,6 +85,36 @@ export type HookFunction<
? (input?: Input) => T
: (input: Input) => T
export type SWRHook<
// Data obj returned by the hook and fetch operation
Data,
// Input expected by the hook
Input extends { [k: string]: unknown } = {},
// Input expected before doing a fetch operation
FetchInput extends HookFetchInput = {},
// Custom state added to the response object of SWR
State = {}
> = {
useHook(
context: SWRHookContext<Data, FetchInput>
): HookFunction<
Input & { swrOptions?: SwrOptions<Data, FetchInput> },
ResponseState<Data> & State
>
fetchOptions: HookFetcherOptions
fetcher?: HookFetcherFn<Data, FetchInput>
}
export type SWRHookContext<
Data,
FetchInput extends { [k: string]: unknown } = {}
> = {
useData(context?: {
input?: HookFetchInput | HookSwrInput
swrOptions?: SwrOptions<Data, FetchInput>
}): ResponseState<Data>
}
export type MutationHook<
// Data obj returned by the hook and fetch operation
Data,
@ -118,9 +150,7 @@ export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface<
*/
export type Prop<T, K extends keyof T> = NonNullable<T[K]>
export type HookHandlerType =
| HookHandler<any, any, any>
| MutationHandler<any, any, any>
export type HookHandlerType = HookHandler<any, any, any>
export type UseHookParameters<H extends HookHandlerType> = Parameters<
Prop<H, 'useHook'>

View File

@ -1,11 +1,11 @@
import useSWR, { responseInterface } from 'swr'
import type {
HookHandler,
HookSwrInput,
HookFetchInput,
PickRequired,
Fetcher,
SwrOptions,
HookFetcherOptions,
HookFetcherFn,
} from './types'
import defineProperty from './define-property'
import { CommerceError } from './errors'
@ -19,7 +19,10 @@ export type UseData = <
Input extends { [k: string]: unknown } = {},
FetchInput extends HookFetchInput = {}
>(
options: PickRequired<HookHandler<Data, Input, FetchInput>, 'fetcher'>,
options: {
fetchOptions: HookFetcherOptions
fetcher: HookFetcherFn<Data, FetchInput>
},
input: HookFetchInput | HookSwrInput,
fetcherFn: Fetcher,
swrOptions?: SwrOptions<Data, FetchInput>

View File

@ -1,43 +1,50 @@
import { useCallback } from 'react'
import type { MutationHook } from './types'
import type { Fetcher, MutationHook, PickRequired, SWRHook } from './types'
import { Provider, useCommerce } from '..'
import useData from './use-data'
export function useHookHandler<P extends Provider>(
fn: (provider: P) => MutationHook<any, any, any>,
fetcher: any
) {
export function useFetcher() {
const { providerRef, fetcherRef } = useCommerce()
return providerRef.current.fetcher ?? fetcherRef.current
}
export function useHook<
P extends Provider,
H extends MutationHook<any, any, any> | SWRHook<any, any, any>
>(fn: (provider: P) => H) {
const { providerRef } = useCommerce<P>()
const provider = providerRef.current
const opts = fn(provider)
const handler =
opts.useHook ??
(() => {
const { fetch } = useHook(fn, fetcher)
return (input: any) => fetch({ input })
})
return handler
return fn(provider)
}
export default function useHook<P extends Provider>(
fn: (provider: P) => MutationHook<any, any, any>,
fetcher: any
export function useSWRHook<H extends SWRHook<any, any, any>>(
hook: PickRequired<H, 'fetcher'>
) {
const { providerRef, fetcherRef } = useCommerce<P>()
const provider = providerRef.current
const opts = fn(provider)
const fetcherFn = opts.fetcher ?? fetcher
const fetchFn = provider.fetcher ?? fetcherRef.current
const fetch = useCallback(
({ input }: { input: any }) => {
return fetcherFn({
input,
options: opts.fetchOptions,
fetch: fetchFn,
})
},
[fetchFn, opts.fetchOptions]
)
const fetcher = useFetcher()
return { fetch }
return hook.useHook({
useData(ctx) {
const response = useData(hook, ctx?.input ?? [], fetcher, ctx?.swrOptions)
return response
},
})
}
export function useMutationHook<H extends MutationHook<any, any, any>>(
hook: PickRequired<H, 'fetcher'>
) {
const fetcher = useFetcher()
return hook.useHook({
fetch: useCallback(
({ input }) => {
return hook.fetcher({
input,
options: hook.fetchOptions,
fetch: fetcher,
})
},
[fetcher, hook.fetchOptions]
),
})
}