mirror of
https://github.com/vercel/commerce.git
synced 2025-03-14 22:42:33 +00:00
Memoize functions in commerce hooks and debounce update
This commit is contained in:
parent
0aac955910
commit
5c4f3e7ff2
@ -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>
|
||||
|
@ -25,6 +25,7 @@
|
||||
"cookie": "^0.4.1",
|
||||
"js-cookie": "^2.2.1",
|
||||
"lodash": "^4.17.20",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"next": "^9.5.4-canary.23",
|
||||
"postcss-nesting": "^7.0.1",
|
||||
"react": "^16.13.1",
|
||||
@ -39,6 +40,7 @@
|
||||
"@graphql-codegen/typescript-operations": "^1.17.8",
|
||||
"@types/cookie": "^0.4.0",
|
||||
"@types/js-cookie": "^2.2.6",
|
||||
"@types/lodash.debounce": "^4.0.6",
|
||||
"@types/node": "^14.11.2",
|
||||
"@types/react": "^16.9.49",
|
||||
"graphql": "^15.3.0",
|
||||
|
17
yarn.lock
17
yarn.lock
@ -1572,6 +1572,18 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/lodash.debounce@^4.0.6":
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash.debounce/-/lodash.debounce-4.0.6.tgz#c5a2326cd3efc46566c47e4c0aa248dc0ee57d60"
|
||||
integrity sha512-4WTmnnhCfDvvuLMaF3KV4Qfki93KebocUF45msxhYyjMttZDQYzHkO639ohhk8+oco2cluAFL3t5+Jn4mleylQ==
|
||||
dependencies:
|
||||
"@types/lodash" "*"
|
||||
|
||||
"@types/lodash@*":
|
||||
version "4.14.161"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.161.tgz#a21ca0777dabc6e4f44f3d07f37b765f54188b18"
|
||||
integrity sha512-EP6O3Jkr7bXvZZSZYlsgt5DIjiGr0dXP1/jVEwVLTFgg0d+3lWVQkRavYVQszV7dYUwvg0B8R0MBDpcmXg7XIA==
|
||||
|
||||
"@types/node@*", "@types/node@^14.11.2":
|
||||
version "14.11.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.2.tgz#2de1ed6670439387da1c9f549a2ade2b0a799256"
|
||||
@ -4703,6 +4715,11 @@ lodash._reinterpolate@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
|
||||
integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=
|
||||
|
||||
lodash.debounce@^4.0.8:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
|
||||
|
||||
lodash.includes@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
||||
|
Loading…
x
Reference in New Issue
Block a user