From d8de84bed139512b9cb18ed619d291fdc1a3667a Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Thu, 11 Feb 2021 13:26:09 -0500 Subject: [PATCH 1/6] Move useCustomer to the new hook --- .../bigcommerce/customer/use-customer.tsx | 40 +----------- framework/bigcommerce/provider.tsx | 24 +++++++ framework/commerce/customer/use-customer.tsx | 62 +++++++++++++++++++ framework/commerce/index.tsx | 6 +- framework/commerce/types.ts | 4 ++ framework/commerce/use-customer.tsx | 5 -- framework/commerce/utils/types.ts | 2 +- framework/commerce/wishlist/use-wishlist.tsx | 8 +-- 8 files changed, 102 insertions(+), 49 deletions(-) create mode 100644 framework/commerce/customer/use-customer.tsx delete mode 100644 framework/commerce/use-customer.tsx diff --git a/framework/bigcommerce/customer/use-customer.tsx b/framework/bigcommerce/customer/use-customer.tsx index f44f16c1f..95adb6fb3 100644 --- a/framework/bigcommerce/customer/use-customer.tsx +++ b/framework/bigcommerce/customer/use-customer.tsx @@ -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 = async ( - options, - _, - fetch -) => { - const data = await fetch({ ...defaultOpts, ...options }) - return data?.customer ?? null -} - -export function extendHook( - customFetcher: typeof fetcher, - swrOptions?: SwrOptions -) { - const useCustomer = () => { - return useCommerceCustomer(defaultOpts, [], customFetcher, { - revalidateOnFocus: false, - ...swrOptions, - }) - } - - useCustomer.extend = extendHook - - return useCustomer -} - -export default extendHook(fetcher) +export default useCustomer as UseCustomer diff --git a/framework/bigcommerce/provider.tsx b/framework/bigcommerce/provider.tsx index 60106f7f8..d3ae4d171 100644 --- a/framework/bigcommerce/provider.tsx +++ b/framework/bigcommerce/provider.tsx @@ -4,6 +4,7 @@ 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 useCustomer from './customer/use-customer' import type { Cart } from './types' @@ -130,6 +131,28 @@ const useWishlist: HookHandler< }, } +const useCustomerHandler: HookHandler< + Customer | null, + {}, + {}, + CustomerData | null, + any +> = { + fetchOptions: { + url: '/api/bigcommerce/customers', + method: 'GET', + }, + normalizer: (data) => data.customer, + useHook({ input, useData }) { + return useData({ + swrOptions: { + revalidateOnFocus: false, + ...input.swrOptions, + }, + }) + }, +} + export const bigcommerceProvider = { locale: 'en-us', cartCookie: 'bc_cartId', @@ -137,6 +160,7 @@ export const bigcommerceProvider = { cartNormalizer: normalizeCart, cart: { useCart }, wishlist: { useWishlist }, + customer: { useCustomer: useCustomerHandler }, } export type BigcommerceProvider = typeof bigcommerceProvider diff --git a/framework/commerce/customer/use-customer.tsx b/framework/commerce/customer/use-customer.tsx new file mode 100644 index 000000000..f1675a544 --- /dev/null +++ b/framework/commerce/customer/use-customer.tsx @@ -0,0 +1,62 @@ +import type { Customer } from '../types' +import type { + Prop, + HookFetcherFn, + UseHookInput, + UseHookResponse, +} from '../utils/types' +import useData from '../utils/use-data-2' +import { Provider, useCommerce } from '..' + +export type UseCustomerHandler

= Prop< + Prop, + 'useCustomer' +> + +export type UseCustomerInput

= UseHookInput< + UseCustomerHandler

+> + +export type CustomerResponse

= UseHookResponse< + UseCustomerHandler

+> + +export type UseCustomer

= Partial< + UseCustomerInput

+> extends UseCustomerInput

+ ? (input?: UseCustomerInput

) => CustomerResponse

+ : (input: UseCustomerInput

) => CustomerResponse

+ +export const fetcher: HookFetcherFn = async ({ + options, + fetch, + normalize, +}) => { + const data = await fetch({ ...options }) + return data && normalize ? normalize(data) : data +} + +export default function useCustomer

( + input: UseCustomerInput

= {} +) { + const { providerRef, fetcherRef } = useCommerce

() + + 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 + }, + }) +} diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index cb4136e3b..ccfc07e66 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -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 } from './types' const Commerce = createContext | {}>({}) @@ -21,6 +20,9 @@ export type Provider = CommerceConfig & { wishlist?: { useWishlist?: HookHandler } + customer: { + useCustomer?: HookHandler + } } export type CommerceProps

= { diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index 743a93e4e..31aaa6fd7 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -1,4 +1,5 @@ import type { Wishlist as BCWishlist } from '@framework/api/wishlist' +import type { Customer as BCCustomer } from '@framework/api/customers' export interface Discount { // The value of the discount, can be an amount or percentage @@ -92,6 +93,9 @@ export interface Cart { // TODO: Properly define this type export interface Wishlist extends BCWishlist {} +// TODO: Properly define this type +export interface Customer extends BCCustomer {} + /** * Cart mutations */ diff --git a/framework/commerce/use-customer.tsx b/framework/commerce/use-customer.tsx deleted file mode 100644 index 8e2ff3ec2..000000000 --- a/framework/commerce/use-customer.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import useData from './utils/use-data' - -const useCustomer = useData - -export default useCustomer diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index dbde3e7ec..47da81a7f 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -79,7 +79,7 @@ export type HookHandler< }): ResponseState & State fetchOptions: HookFetcherOptions fetcher?: HookFetcherFn - normalizer?(data: Result): Data + normalizer?(data: NonNullable): Data } export type SwrOptions = ConfigInterface< diff --git a/framework/commerce/wishlist/use-wishlist.tsx b/framework/commerce/wishlist/use-wishlist.tsx index c2e0d2dc1..64bb5a1c1 100644 --- a/framework/commerce/wishlist/use-wishlist.tsx +++ b/framework/commerce/wishlist/use-wishlist.tsx @@ -22,10 +22,10 @@ export type WishlistResponse

= UseHookResponse< > export type UseWishlist

= Partial< - WishlistResponse

-> extends WishlistResponse

- ? (input?: WishlistResponse

) => WishlistResponse

- : (input: WishlistResponse

) => WishlistResponse

+ UseWishlistInput

+> extends UseWishlistInput

+ ? (input?: UseWishlistInput

) => WishlistResponse

+ : (input: UseWishlistInput

) => WishlistResponse

export const fetcher: HookFetcherFn = async ({ options, From 883fbcbcb941972b044227f03f3a88ce4b48521e Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Thu, 11 Feb 2021 13:35:17 -0500 Subject: [PATCH 2/6] Added a default fetcher --- framework/commerce/customer/use-customer.tsx | 10 ++-------- framework/commerce/utils/default-fetcher.ts | 12 ++++++++++++ framework/commerce/wishlist/use-wishlist.tsx | 10 ++-------- 3 files changed, 16 insertions(+), 16 deletions(-) create mode 100644 framework/commerce/utils/default-fetcher.ts diff --git a/framework/commerce/customer/use-customer.tsx b/framework/commerce/customer/use-customer.tsx index f1675a544..4fb9b430b 100644 --- a/framework/commerce/customer/use-customer.tsx +++ b/framework/commerce/customer/use-customer.tsx @@ -5,6 +5,7 @@ import type { UseHookInput, UseHookResponse, } from '../utils/types' +import defaultFetcher from '../utils/default-fetcher' import useData from '../utils/use-data-2' import { Provider, useCommerce } from '..' @@ -27,14 +28,7 @@ export type UseCustomer

= Partial< ? (input?: UseCustomerInput

) => CustomerResponse

: (input: UseCustomerInput

) => CustomerResponse

-export const fetcher: HookFetcherFn = async ({ - options, - fetch, - normalize, -}) => { - const data = await fetch({ ...options }) - return data && normalize ? normalize(data) : data -} +export const fetcher = defaultFetcher as HookFetcherFn export default function useCustomer

( input: UseCustomerInput

= {} diff --git a/framework/commerce/utils/default-fetcher.ts b/framework/commerce/utils/default-fetcher.ts new file mode 100644 index 000000000..25211a689 --- /dev/null +++ b/framework/commerce/utils/default-fetcher.ts @@ -0,0 +1,12 @@ +import { HookFetcherFn } from './types' + +const defaultFetcher: HookFetcherFn = async ({ + options, + fetch, + normalize, +}) => { + const data = await fetch({ ...options }) + return data && normalize ? normalize(data) : data +} + +export default defaultFetcher diff --git a/framework/commerce/wishlist/use-wishlist.tsx b/framework/commerce/wishlist/use-wishlist.tsx index 64bb5a1c1..314f0a1c2 100644 --- a/framework/commerce/wishlist/use-wishlist.tsx +++ b/framework/commerce/wishlist/use-wishlist.tsx @@ -5,6 +5,7 @@ import type { UseHookInput, UseHookResponse, } from '../utils/types' +import defaultFetcher from '../utils/default-fetcher' import useData from '../utils/use-data-2' import { Provider, useCommerce } from '..' @@ -27,14 +28,7 @@ export type UseWishlist

= Partial< ? (input?: UseWishlistInput

) => WishlistResponse

: (input: UseWishlistInput

) => WishlistResponse

-export const fetcher: HookFetcherFn = async ({ - options, - fetch, - normalize, -}) => { - const data = await fetch({ ...options }) - return data && normalize ? normalize(data) : data -} +export const fetcher = defaultFetcher as HookFetcherFn export default function useWishlist

( input: UseWishlistInput

= {} From 1549368c888b0b59fd688ffcef09f7d2ece73d00 Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Fri, 12 Feb 2021 12:17:36 -0500 Subject: [PATCH 3/6] Moved useSearch to the new hook --- framework/bigcommerce/product/use-search.tsx | 65 +------------------- framework/bigcommerce/provider.tsx | 52 ++++++++++++++++ framework/commerce/index.tsx | 5 +- framework/commerce/products/use-search.tsx | 58 ++++++++++++++++- framework/commerce/types.ts | 4 ++ 5 files changed, 118 insertions(+), 66 deletions(-) diff --git a/framework/bigcommerce/product/use-search.tsx b/framework/bigcommerce/product/use-search.tsx index 2b4ee7593..52db6a72d 100644 --- a/framework/bigcommerce/product/use-search.tsx +++ b/framework/bigcommerce/product/use-search.tsx @@ -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 = ( - 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 -) { - 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 diff --git a/framework/bigcommerce/provider.tsx b/framework/bigcommerce/provider.tsx index d3ae4d171..653a28a87 100644 --- a/framework/bigcommerce/provider.tsx +++ b/framework/bigcommerce/provider.tsx @@ -5,6 +5,7 @@ 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' @@ -153,6 +154,56 @@ const useCustomerHandler: HookHandler< }, } +export type SearchProductsInput = { + search?: string + categoryId?: number + brandId?: number + sort?: string +} + +const useSearch: HookHandler< + SearchProductsData, + SearchProductsInput, + SearchProductsInput, + any, + any +> = { + 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', @@ -161,6 +212,7 @@ export const bigcommerceProvider = { cart: { useCart }, wishlist: { useWishlist }, customer: { useCustomer: useCustomerHandler }, + products: { useSearch }, } export type BigcommerceProvider = typeof bigcommerceProvider diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index ccfc07e66..d8d882f93 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -8,7 +8,7 @@ import { } from 'react' import { Fetcher, HookHandler } from './utils/types' import type { FetchCartInput } from './cart/use-cart' -import type { Cart, Wishlist, Customer } from './types' +import type { Cart, Wishlist, Customer, SearchProductsData } from './types' const Commerce = createContext | {}>({}) @@ -23,6 +23,9 @@ export type Provider = CommerceConfig & { customer: { useCustomer?: HookHandler } + products: { + useSearch?: HookHandler + } } export type CommerceProps

= { diff --git a/framework/commerce/products/use-search.tsx b/framework/commerce/products/use-search.tsx index 637c8a899..9971c309d 100644 --- a/framework/commerce/products/use-search.tsx +++ b/framework/commerce/products/use-search.tsx @@ -1,5 +1,57 @@ -import useData from '../utils/use-data' +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-2' +import { Provider, useCommerce } from '..' +import { BigcommerceProvider } from '@framework' -const useSearch = useData +export type UseSearchHandler

= Prop< + Prop, + 'useSearch' +> -export default useSearch +export type UseSeachInput

= UseHookInput< + UseSearchHandler

+> + +export type SearchResponse

= UseHookResponse< + UseSearchHandler

+> + +export type UseSearch

= Partial< + UseSeachInput

+> extends UseSeachInput

+ ? (input?: UseSeachInput

) => SearchResponse

+ : (input: UseSeachInput

) => SearchResponse

+ +export const fetcher = defaultFetcher as HookFetcherFn + +export default function useSearch

( + input: UseSeachInput

= {} +) { + const { providerRef, fetcherRef } = useCommerce

() + + 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 + }, + }) +} diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index 31aaa6fd7..41aedb228 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -1,5 +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 @@ -96,6 +97,9 @@ 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 */ From 505d3fe04bbb21031ec67b43af56fbd0a384f120 Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Fri, 12 Feb 2021 13:01:13 -0500 Subject: [PATCH 4/6] Removed old use-data lib --- framework/commerce/cart/use-cart.tsx | 2 +- framework/commerce/customer/use-customer.tsx | 2 +- framework/commerce/products/use-search.tsx | 2 +- framework/commerce/utils/use-data-2.ts | 84 -------------------- framework/commerce/utils/use-data.tsx | 58 ++++++++------ framework/commerce/wishlist/use-wishlist.tsx | 2 +- 6 files changed, 38 insertions(+), 112 deletions(-) delete mode 100644 framework/commerce/utils/use-data-2.ts diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index 6b1a3c789..a1f1d0f84 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -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 = { diff --git a/framework/commerce/customer/use-customer.tsx b/framework/commerce/customer/use-customer.tsx index 4fb9b430b..25112128e 100644 --- a/framework/commerce/customer/use-customer.tsx +++ b/framework/commerce/customer/use-customer.tsx @@ -6,7 +6,7 @@ import type { UseHookResponse, } from '../utils/types' import defaultFetcher from '../utils/default-fetcher' -import useData from '../utils/use-data-2' +import useData from '../utils/use-data' import { Provider, useCommerce } from '..' export type UseCustomerHandler

= Prop< diff --git a/framework/commerce/products/use-search.tsx b/framework/commerce/products/use-search.tsx index 9971c309d..1f887f5fe 100644 --- a/framework/commerce/products/use-search.tsx +++ b/framework/commerce/products/use-search.tsx @@ -6,7 +6,7 @@ import type { UseHookResponse, } from '../utils/types' import defaultFetcher from '../utils/default-fetcher' -import useData from '../utils/use-data-2' +import useData from '../utils/use-data' import { Provider, useCommerce } from '..' import { BigcommerceProvider } from '@framework' diff --git a/framework/commerce/utils/use-data-2.ts b/framework/commerce/utils/use-data-2.ts deleted file mode 100644 index cc4d2cc5b..000000000 --- a/framework/commerce/utils/use-data-2.ts +++ /dev/null @@ -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 = responseInterface & { - isLoading: boolean -} - -export type UseData = < - Data = any, - Input extends { [k: string]: unknown } = {}, - FetchInput extends HookFetchInput = {}, - Result = any, - Body = any ->( - options: PickRequired< - HookHandler, - 'fetcher' - >, - input: HookFetchInput | HookSwrInput, - fetcherFn: Fetcher, - swrOptions?: SwrOptions -) => ResponseState - -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 diff --git a/framework/commerce/utils/use-data.tsx b/framework/commerce/utils/use-data.tsx index 58a1a0a47..cc4d2cc5b 100644 --- a/framework/commerce/utils/use-data.tsx +++ b/framework/commerce/utils/use-data.tsx @@ -1,44 +1,54 @@ -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 = ConfigInterface< - Data, - CommerceError, - HookFetcher -> export type ResponseState = responseInterface & { isLoading: boolean } -export type UseData = ( - options: HookFetcherOptions | (() => HookFetcherOptions | null), - input: HookSwrInput, - fetcherFn: HookFetcher, - swrOptions?: SwrOptions +export type UseData = < + Data = any, + Input extends { [k: string]: unknown } = {}, + FetchInput extends HookFetchInput = {}, + Result = any, + Body = any +>( + options: PickRequired< + HookHandler, + 'fetcher' + >, + input: HookFetchInput | HookSwrInput, + fetcherFn: Fetcher, + swrOptions?: SwrOptions ) => ResponseState 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, + 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 @@ -50,9 +60,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, diff --git a/framework/commerce/wishlist/use-wishlist.tsx b/framework/commerce/wishlist/use-wishlist.tsx index 314f0a1c2..dc912bc98 100644 --- a/framework/commerce/wishlist/use-wishlist.tsx +++ b/framework/commerce/wishlist/use-wishlist.tsx @@ -6,7 +6,7 @@ import type { UseHookResponse, } from '../utils/types' import defaultFetcher from '../utils/default-fetcher' -import useData from '../utils/use-data-2' +import useData from '../utils/use-data' import { Provider, useCommerce } from '..' export type UseWishlistHandler

= Prop< From b907c31ef2b67c813ce4f010cc43864e4fe259f3 Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Fri, 12 Feb 2021 13:13:08 -0500 Subject: [PATCH 5/6] Removed generics for result and body --- framework/bigcommerce/provider.tsx | 26 ++++++++------------- framework/commerce/cart/use-cart.tsx | 4 +--- framework/commerce/utils/default-fetcher.ts | 12 +++------- framework/commerce/utils/types.ts | 12 +++------- framework/commerce/utils/use-data.tsx | 12 +++------- 5 files changed, 20 insertions(+), 46 deletions(-) diff --git a/framework/bigcommerce/provider.tsx b/framework/bigcommerce/provider.tsx index 653a28a87..6fd02e304 100644 --- a/framework/bigcommerce/provider.tsx +++ b/framework/bigcommerce/provider.tsx @@ -50,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 }, @@ -83,8 +84,6 @@ const useWishlist: HookHandler< Wishlist | null, { includeProducts?: boolean }, { customerId?: number; includeProducts: boolean }, - any, - any, { isEmpty?: boolean } > = { fetchOptions: { @@ -132,18 +131,15 @@ const useWishlist: HookHandler< }, } -const useCustomerHandler: HookHandler< - Customer | null, - {}, - {}, - CustomerData | null, - any -> = { +const useCustomerHandler: HookHandler = { fetchOptions: { url: '/api/bigcommerce/customers', method: 'GET', }, - normalizer: (data) => data.customer, + async fetcher({ options, fetch }) { + const data = await fetch(options) + return data?.customer ?? null + }, useHook({ input, useData }) { return useData({ swrOptions: { @@ -164,9 +160,7 @@ export type SearchProductsInput = { const useSearch: HookHandler< SearchProductsData, SearchProductsInput, - SearchProductsInput, - any, - any + SearchProductsInput > = { fetchOptions: { url: '/api/bigcommerce/catalog/products', diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index a1f1d0f84..f7b384047 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -34,10 +34,8 @@ export const fetcher: HookFetcherFn = 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

( diff --git a/framework/commerce/utils/default-fetcher.ts b/framework/commerce/utils/default-fetcher.ts index 25211a689..8dc9def75 100644 --- a/framework/commerce/utils/default-fetcher.ts +++ b/framework/commerce/utils/default-fetcher.ts @@ -1,12 +1,6 @@ -import { HookFetcherFn } from './types' +import type { HookFetcherFn } from './types' -const defaultFetcher: HookFetcherFn = async ({ - options, - fetch, - normalize, -}) => { - const data = await fetch({ ...options }) - return data && normalize ? normalize(data) : data -} +const defaultFetcher: HookFetcherFn = ({ options, fetch }) => + fetch(options) export default defaultFetcher diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 47da81a7f..98e4ebbae 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -40,7 +40,6 @@ export type HookFetcherFn< options: HookFetcherOptions input: Input fetch: (options: FetcherOptions) => Promise - normalize?(data: Result): Data }) => Data | Promise 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 } + input: Input & { swrOptions?: SwrOptions } useData(context?: { input?: HookFetchInput | HookSwrInput - swrOptions?: SwrOptions + swrOptions?: SwrOptions }): ResponseState }): ResponseState & State fetchOptions: HookFetcherOptions - fetcher?: HookFetcherFn - normalizer?(data: NonNullable): Data + fetcher?: HookFetcherFn } export type SwrOptions = ConfigInterface< diff --git a/framework/commerce/utils/use-data.tsx b/framework/commerce/utils/use-data.tsx index cc4d2cc5b..94679a0c6 100644 --- a/framework/commerce/utils/use-data.tsx +++ b/framework/commerce/utils/use-data.tsx @@ -17,17 +17,12 @@ export type ResponseState = responseInterface & { export type UseData = < Data = any, Input extends { [k: string]: unknown } = {}, - FetchInput extends HookFetchInput = {}, - Result = any, - Body = any + FetchInput extends HookFetchInput = {} >( - options: PickRequired< - HookHandler, - 'fetcher' - >, + options: PickRequired, 'fetcher'>, input: HookFetchInput | HookSwrInput, fetcherFn: Fetcher, - swrOptions?: SwrOptions + swrOptions?: SwrOptions ) => ResponseState const useData: UseData = (options, input, fetcherFn, swrOptions) => { @@ -47,7 +42,6 @@ const useData: UseData = (options, input, fetcherFn, swrOptions) => { return obj }, {}), fetch: fetcherFn, - normalize: options.normalizer, }) } catch (error) { // SWR will not log errors, but any error that's not an instance From b116c0cfe1528ecb52c7507f03a4c215c6b08164 Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Fri, 12 Feb 2021 13:20:17 -0500 Subject: [PATCH 6/6] Removed normalizr --- package.json | 1 - yarn.lock | 5 ----- 2 files changed, 6 deletions(-) diff --git a/package.json b/package.json index ff492a35e..287af0aa4 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "next": "^10.0.5", "next-seo": "^4.11.0", "next-themes": "^0.0.4", - "normalizr": "^3.6.1", "postcss-nesting": "^7.0.1", "react": "^16.14.0", "react-dom": "^16.14.0", diff --git a/yarn.lock b/yarn.lock index b46f7acce..55e40e4ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5374,11 +5374,6 @@ normalize.css@^8.0.1: resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3" integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg== -normalizr@^3.6.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/normalizr/-/normalizr-3.6.1.tgz#d367ab840e031ff382141b8d81ce279292ff69fe" - integrity sha512-8iEmqXmPtll8PwbEFrbPoDxVw7MKnNvt3PZzR2Xvq9nggEEOgBlNICPXYzyZ4w4AkHUzCU998mdatER3n2VaMA== - npm-run-path@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"