mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 20:26:49 +00:00
Memoize functions in commerce hooks and debounce update
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { useCallback } from 'react'
|
||||
import type { Fetcher } from '@lib/commerce'
|
||||
import { default as useCartAddItem } from '@lib/commerce/cart/use-add-item'
|
||||
import type { ItemBody, AddItemBody } from '../api/cart'
|
||||
@@ -21,11 +22,13 @@ function fetcher(fetch: Fetcher<Cart>, { item }: AddItemBody) {
|
||||
export default function useAddItem() {
|
||||
const { mutate } = useCart()
|
||||
const fn = useCartAddItem<Cart, AddItemBody>(fetcher)
|
||||
const addItem = async (input: UpdateItemInput) => {
|
||||
const data = await fn({ item: input })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
}
|
||||
|
||||
return addItem
|
||||
return useCallback(
|
||||
async function addItem(input: UpdateItemInput) {
|
||||
const data = await fn({ item: input })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
},
|
||||
[fn, mutate]
|
||||
)
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { useCallback } from 'react'
|
||||
import type { Fetcher } from '@lib/commerce'
|
||||
import { default as useCartRemoveItem } from '@lib/commerce/cart/use-remove-item'
|
||||
import type { RemoveItemBody } from '../api/cart'
|
||||
@@ -21,11 +22,13 @@ export function fetcher(
|
||||
export default function useRemoveItem(item?: any) {
|
||||
const { mutate } = useCart()
|
||||
const fn = useCartRemoveItem<Cart | null, RemoveItemBody>(fetcher)
|
||||
const removeItem = async (input: RemoveItemInput) => {
|
||||
const data = await fn({ itemId: input.id ?? item?.id })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
}
|
||||
|
||||
return removeItem
|
||||
return useCallback(
|
||||
async function removeItem(input: RemoveItemInput) {
|
||||
const data = await fn({ itemId: input.id ?? item?.id })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
},
|
||||
[fn, mutate]
|
||||
)
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import { useCallback } from 'react'
|
||||
import debounce from 'lodash.debounce'
|
||||
import type { Fetcher } from '@lib/commerce'
|
||||
import { default as useCartUpdateItem } from '@lib/commerce/cart/use-update-item'
|
||||
import type { ItemBody, UpdateItemBody } from '../api/cart'
|
||||
@@ -26,21 +28,23 @@ function fetcher(
|
||||
})
|
||||
}
|
||||
|
||||
export default function useUpdateItem(item?: any) {
|
||||
export default function useUpdateItem(item?: any, cfg?: { wait?: number }) {
|
||||
const { mutate } = useCart()
|
||||
const fn = useCartUpdateItem<Cart | null, UpdateItemBody>(fetcher)
|
||||
const updateItem = async (input: UpdateItemInput) => {
|
||||
const data = await fn({
|
||||
itemId: input.id ?? item?.id,
|
||||
item: {
|
||||
productId: input.productId ?? item?.product_id,
|
||||
variantId: input.productId ?? item?.variant_id,
|
||||
quantity: input.quantity,
|
||||
},
|
||||
})
|
||||
await mutate(data, false)
|
||||
return data
|
||||
}
|
||||
|
||||
return updateItem
|
||||
return useCallback(
|
||||
debounce(async (input: UpdateItemInput) => {
|
||||
const data = await fn({
|
||||
itemId: input.id ?? item?.id,
|
||||
item: {
|
||||
productId: input.productId ?? item?.product_id,
|
||||
variantId: input.productId ?? item?.variant_id,
|
||||
quantity: input.quantity,
|
||||
},
|
||||
})
|
||||
await mutate(data, false)
|
||||
return data
|
||||
}, cfg?.wait ?? 500),
|
||||
[fn, mutate]
|
||||
)
|
||||
}
|
||||
|
@@ -19,10 +19,9 @@ const CartProvider: FC<CartProviderProps> = ({ children, query, url }) => {
|
||||
}
|
||||
|
||||
function useCart<C>() {
|
||||
const { fetcher: fetch, cartCookie } = useCommerce()
|
||||
const fetcher = (url?: string, query?: string) => {
|
||||
return Cookies.get(cartCookie) ? fetch({ url, query }) : null
|
||||
}
|
||||
const { fetcherRef, cartCookie } = useCommerce()
|
||||
const fetcher = (url?: string, query?: string) =>
|
||||
Cookies.get(cartCookie) ? fetcherRef.current({ url, query }) : null
|
||||
const { url, query } = useContext(CartContext)
|
||||
const response = useSWR([url, query], fetcher, {
|
||||
revalidateOnFocus: false,
|
||||
|
@@ -1,11 +1,15 @@
|
||||
import { useCallback } from 'react'
|
||||
import { Fetcher, useCommerce } from '..'
|
||||
|
||||
export default function useAddItem<T, Input>(
|
||||
fetcher: (fetch: Fetcher<T>, input: Input) => T | Promise<T>
|
||||
) {
|
||||
const { fetcher: fetch } = useCommerce()
|
||||
const { fetcherRef } = useCommerce()
|
||||
|
||||
return async function addItem(input: Input) {
|
||||
return fetcher(fetch, input)
|
||||
}
|
||||
return useCallback(
|
||||
function addItem(input: Input) {
|
||||
return fetcher(fetcherRef.current, input)
|
||||
},
|
||||
[fetcher]
|
||||
)
|
||||
}
|
||||
|
@@ -1,11 +1,15 @@
|
||||
import { useCallback } from 'react'
|
||||
import { Fetcher, useCommerce } from '..'
|
||||
|
||||
export default function useRemoveItem<T, Input>(
|
||||
fetcher: (fetch: Fetcher<T>, input: Input) => T | Promise<T>
|
||||
) {
|
||||
const { fetcher: fetch } = useCommerce()
|
||||
const { fetcherRef } = useCommerce()
|
||||
|
||||
return async function removeItem(input: Input) {
|
||||
return fetcher(fetch, input)
|
||||
}
|
||||
return useCallback(
|
||||
function removeItem(input: Input) {
|
||||
return fetcher(fetcherRef.current, input)
|
||||
},
|
||||
[fetcher]
|
||||
)
|
||||
}
|
||||
|
@@ -1,11 +1,15 @@
|
||||
import { useCallback } from 'react'
|
||||
import { Fetcher, useCommerce } from '..'
|
||||
|
||||
export default function useUpdateItem<T, Input>(
|
||||
fetcher: (fetch: Fetcher<T>, input: Input) => T | Promise<T>
|
||||
) {
|
||||
const { fetcher: fetch } = useCommerce()
|
||||
const { fetcherRef } = useCommerce()
|
||||
|
||||
return async function updateItem(input: Input) {
|
||||
return fetcher(fetch, input)
|
||||
}
|
||||
return useCallback(
|
||||
function updateItem(input: Input) {
|
||||
return fetcher(fetcherRef.current, input)
|
||||
},
|
||||
[fetcher]
|
||||
)
|
||||
}
|
||||
|
@@ -1,14 +1,21 @@
|
||||
import { createContext, ReactNode, useContext, useMemo } from 'react'
|
||||
import {
|
||||
ReactNode,
|
||||
MutableRefObject,
|
||||
createContext,
|
||||
useContext,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from 'react'
|
||||
|
||||
const Commerce = createContext<CommerceConfig | null>(null)
|
||||
|
||||
export type CommerceProps = {
|
||||
children?: ReactNode
|
||||
config: CommerceConfig
|
||||
config: { fetcher: Fetcher<any> } & CommerceConfig
|
||||
}
|
||||
|
||||
export type CommerceConfig = {
|
||||
fetcher: Fetcher<any>
|
||||
fetcherRef: MutableRefObject<any>
|
||||
locale: string
|
||||
cartCookie: string
|
||||
}
|
||||
@@ -28,17 +35,16 @@ export function CommerceProvider({ children, config }: CommerceProps) {
|
||||
throw new Error('CommerceProvider requires a valid config object')
|
||||
}
|
||||
|
||||
const fetcherRef = useRef(config.fetcher)
|
||||
// Because the config is an object, if the parent re-renders this provider
|
||||
// will re-render every consumer unless we memoize the config
|
||||
const cfg = useMemo(
|
||||
() => ({
|
||||
fetcher: config.fetcher,
|
||||
fetcherRef,
|
||||
locale: config.locale,
|
||||
cartCookie: config.cartCookie,
|
||||
}),
|
||||
// Even though the fetcher is a function, it's never expected to be
|
||||
// added dynamically (We should say that on the docs for this hook)
|
||||
[config.fetcher, config.locale, config.cartCookie]
|
||||
[config.locale, config.cartCookie]
|
||||
)
|
||||
|
||||
return <Commerce.Provider value={cfg}>{children}</Commerce.Provider>
|
||||
|
Reference in New Issue
Block a user