4
0
forked from crowetic/commerce

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 { 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 useCart, { UseCart, FetchCartInput } from '@commerce/cart/use-cart'
import { normalizeCart } from '../lib/normalize' import { normalizeCart } from '../lib/normalize'
import type { Cart } from '../types' 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, Cart | null,
{}, {},
FetchCartInput, FetchCartInput,
@ -21,9 +20,9 @@ export const handler: HookHandler<
const data = cartId ? await fetch(options) : null const data = cartId ? await fetch(options) : null
return data && normalizeCart(data) return data && normalizeCart(data)
}, },
useHook({ input, useData }) { useHook: ({ useData }) => (input) => {
const response = useData({ const response = useData({
swrOptions: { revalidateOnFocus: false, ...input.swrOptions }, swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
}) })
return useMemo( return useMemo(

View File

@ -46,7 +46,7 @@ export const handler = {
ctx: { item?: T } = {} ctx: { item?: T } = {}
) => { ) => {
const { item } = ctx const { item } = ctx
const { mutate } = useCart() as any const { mutate } = useCart()
const removeItem: RemoveItemFn<LineItem> = async (input) => { const removeItem: RemoveItemFn<LineItem> = async (input) => {
const itemId = input?.id ?? item?.id 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 { MutationHook, HookFetcherFn } from '../utils/types'
import type { Cart, CartItemBody, AddCartItemBody } from '../types' import type { Cart, CartItemBody, AddCartItemBody } from '../types'
import type { Provider } from '..' import type { Provider } from '..'
@ -6,9 +7,7 @@ import type { Provider } from '..'
export const fetcher: HookFetcherFn< export const fetcher: HookFetcherFn<
Cart, Cart,
AddCartItemBody<CartItemBody> AddCartItemBody<CartItemBody>
> = async ({ options, input, fetch }) => { > = mutationFetcher
return fetch({ ...options, body: input })
}
export type UseAddItem< export type UseAddItem<
H extends MutationHook<any, any, any> = MutationHook<Cart, {}, CartItemBody> H extends MutationHook<any, any, any> = MutationHook<Cart, {}, CartItemBody>
@ -17,8 +16,8 @@ export type UseAddItem<
const fn = (provider: Provider) => provider.cart?.useAddItem! const fn = (provider: Provider) => provider.cart?.useAddItem!
const useAddItem: UseAddItem = (...args) => { const useAddItem: UseAddItem = (...args) => {
const handler = useHookHandler(fn, fetcher) const hook = useHook(fn)
return handler(useHook(fn, fetcher))(...args) return useMutationHook({ fetcher, ...hook })(...args)
} }
export default useAddItem export default useAddItem

View File

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

View File

@ -6,7 +6,7 @@ import {
useMemo, useMemo,
useRef, useRef,
} from 'react' } 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 { FetchCartInput } from './cart/use-cart'
import type { Cart, Wishlist, Customer, SearchProductsData } from './types' import type { Cart, Wishlist, Customer, SearchProductsData } from './types'
@ -15,7 +15,7 @@ const Commerce = createContext<CommerceContextValue<any> | {}>({})
export type Provider = CommerceConfig & { export type Provider = CommerceConfig & {
fetcher: Fetcher fetcher: Fetcher
cart?: { cart?: {
useCart?: HookHandler<Cart | null, any, FetchCartInput> useCart?: SWRHook<Cart | null, any, FetchCartInput>
useAddItem?: MutationHook<any, any, any> useAddItem?: MutationHook<any, any, any>
useUpdateItem?: MutationHook<any, any, any> useUpdateItem?: MutationHook<any, any, any>
useRemoveItem?: 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 }) => const defaultFetcher: HookFetcherFn<any> = ({ options, fetch }) =>
fetch(options) fetch(options)
export const mutationFetcher: HookFetcherFn<any> = ({ export const mutationFetcher: HookFetcherFn<any, any> = ({
input, input,
options, options,
fetch, 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 * 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> & 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 * Core fetcher added by CommerceProvider
@ -83,6 +85,36 @@ export type HookFunction<
? (input?: Input) => T ? (input?: Input) => T
: (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< export type MutationHook<
// Data obj returned by the hook and fetch operation // Data obj returned by the hook and fetch operation
Data, 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 Prop<T, K extends keyof T> = NonNullable<T[K]>
export type HookHandlerType = export type HookHandlerType = HookHandler<any, any, any>
| HookHandler<any, any, any>
| MutationHandler<any, any, any>
export type UseHookParameters<H extends HookHandlerType> = Parameters< export type UseHookParameters<H extends HookHandlerType> = Parameters<
Prop<H, 'useHook'> Prop<H, 'useHook'>

View File

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

View File

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