forked from crowetic/commerce
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 type { Fetcher } from '@lib/commerce'
|
||||||
import { default as useCartAddItem } from '@lib/commerce/cart/use-add-item'
|
import { default as useCartAddItem } from '@lib/commerce/cart/use-add-item'
|
||||||
import type { ItemBody, AddItemBody } from '../api/cart'
|
import type { ItemBody, AddItemBody } from '../api/cart'
|
||||||
@ -21,11 +22,13 @@ function fetcher(fetch: Fetcher<Cart>, { item }: AddItemBody) {
|
|||||||
export default function useAddItem() {
|
export default function useAddItem() {
|
||||||
const { mutate } = useCart()
|
const { mutate } = useCart()
|
||||||
const fn = useCartAddItem<Cart, AddItemBody>(fetcher)
|
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 type { Fetcher } from '@lib/commerce'
|
||||||
import { default as useCartRemoveItem } from '@lib/commerce/cart/use-remove-item'
|
import { default as useCartRemoveItem } from '@lib/commerce/cart/use-remove-item'
|
||||||
import type { RemoveItemBody } from '../api/cart'
|
import type { RemoveItemBody } from '../api/cart'
|
||||||
@ -21,11 +22,13 @@ export function fetcher(
|
|||||||
export default function useRemoveItem(item?: any) {
|
export default function useRemoveItem(item?: any) {
|
||||||
const { mutate } = useCart()
|
const { mutate } = useCart()
|
||||||
const fn = useCartRemoveItem<Cart | null, RemoveItemBody>(fetcher)
|
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 type { Fetcher } from '@lib/commerce'
|
||||||
import { default as useCartUpdateItem } from '@lib/commerce/cart/use-update-item'
|
import { default as useCartUpdateItem } from '@lib/commerce/cart/use-update-item'
|
||||||
import type { ItemBody, UpdateItemBody } from '../api/cart'
|
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 { mutate } = useCart()
|
||||||
const fn = useCartUpdateItem<Cart | null, UpdateItemBody>(fetcher)
|
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>() {
|
function useCart<C>() {
|
||||||
const { fetcher: fetch, cartCookie } = useCommerce()
|
const { fetcherRef, cartCookie } = useCommerce()
|
||||||
const fetcher = (url?: string, query?: string) => {
|
const fetcher = (url?: string, query?: string) =>
|
||||||
return Cookies.get(cartCookie) ? fetch({ url, query }) : null
|
Cookies.get(cartCookie) ? fetcherRef.current({ url, query }) : null
|
||||||
}
|
|
||||||
const { url, query } = useContext(CartContext)
|
const { url, query } = useContext(CartContext)
|
||||||
const response = useSWR([url, query], fetcher, {
|
const response = useSWR([url, query], fetcher, {
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
|
import { useCallback } from 'react'
|
||||||
import { Fetcher, useCommerce } from '..'
|
import { Fetcher, useCommerce } from '..'
|
||||||
|
|
||||||
export default function useAddItem<T, Input>(
|
export default function useAddItem<T, Input>(
|
||||||
fetcher: (fetch: Fetcher<T>, input: Input) => T | Promise<T>
|
fetcher: (fetch: Fetcher<T>, input: Input) => T | Promise<T>
|
||||||
) {
|
) {
|
||||||
const { fetcher: fetch } = useCommerce()
|
const { fetcherRef } = useCommerce()
|
||||||
|
|
||||||
return async function addItem(input: Input) {
|
return useCallback(
|
||||||
return fetcher(fetch, input)
|
function addItem(input: Input) {
|
||||||
}
|
return fetcher(fetcherRef.current, input)
|
||||||
|
},
|
||||||
|
[fetcher]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
|
import { useCallback } from 'react'
|
||||||
import { Fetcher, useCommerce } from '..'
|
import { Fetcher, useCommerce } from '..'
|
||||||
|
|
||||||
export default function useRemoveItem<T, Input>(
|
export default function useRemoveItem<T, Input>(
|
||||||
fetcher: (fetch: Fetcher<T>, input: Input) => T | Promise<T>
|
fetcher: (fetch: Fetcher<T>, input: Input) => T | Promise<T>
|
||||||
) {
|
) {
|
||||||
const { fetcher: fetch } = useCommerce()
|
const { fetcherRef } = useCommerce()
|
||||||
|
|
||||||
return async function removeItem(input: Input) {
|
return useCallback(
|
||||||
return fetcher(fetch, input)
|
function removeItem(input: Input) {
|
||||||
}
|
return fetcher(fetcherRef.current, input)
|
||||||
|
},
|
||||||
|
[fetcher]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
|
import { useCallback } from 'react'
|
||||||
import { Fetcher, useCommerce } from '..'
|
import { Fetcher, useCommerce } from '..'
|
||||||
|
|
||||||
export default function useUpdateItem<T, Input>(
|
export default function useUpdateItem<T, Input>(
|
||||||
fetcher: (fetch: Fetcher<T>, input: Input) => T | Promise<T>
|
fetcher: (fetch: Fetcher<T>, input: Input) => T | Promise<T>
|
||||||
) {
|
) {
|
||||||
const { fetcher: fetch } = useCommerce()
|
const { fetcherRef } = useCommerce()
|
||||||
|
|
||||||
return async function updateItem(input: Input) {
|
return useCallback(
|
||||||
return fetcher(fetch, input)
|
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)
|
const Commerce = createContext<CommerceConfig | null>(null)
|
||||||
|
|
||||||
export type CommerceProps = {
|
export type CommerceProps = {
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
config: CommerceConfig
|
config: { fetcher: Fetcher<any> } & CommerceConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CommerceConfig = {
|
export type CommerceConfig = {
|
||||||
fetcher: Fetcher<any>
|
fetcherRef: MutableRefObject<any>
|
||||||
locale: string
|
locale: string
|
||||||
cartCookie: string
|
cartCookie: string
|
||||||
}
|
}
|
||||||
@ -28,17 +35,16 @@ export function CommerceProvider({ children, config }: CommerceProps) {
|
|||||||
throw new Error('CommerceProvider requires a valid config object')
|
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
|
// Because the config is an object, if the parent re-renders this provider
|
||||||
// will re-render every consumer unless we memoize the config
|
// will re-render every consumer unless we memoize the config
|
||||||
const cfg = useMemo(
|
const cfg = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
fetcher: config.fetcher,
|
fetcherRef,
|
||||||
locale: config.locale,
|
locale: config.locale,
|
||||||
cartCookie: config.cartCookie,
|
cartCookie: config.cartCookie,
|
||||||
}),
|
}),
|
||||||
// Even though the fetcher is a function, it's never expected to be
|
[config.locale, config.cartCookie]
|
||||||
// added dynamically (We should say that on the docs for this hook)
|
|
||||||
[config.fetcher, config.locale, config.cartCookie]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return <Commerce.Provider value={cfg}>{children}</Commerce.Provider>
|
return <Commerce.Provider value={cfg}>{children}</Commerce.Provider>
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
"cookie": "^0.4.1",
|
"cookie": "^0.4.1",
|
||||||
"js-cookie": "^2.2.1",
|
"js-cookie": "^2.2.1",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
|
"lodash.debounce": "^4.0.8",
|
||||||
"next": "^9.5.4-canary.23",
|
"next": "^9.5.4-canary.23",
|
||||||
"postcss-nesting": "^7.0.1",
|
"postcss-nesting": "^7.0.1",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
@ -39,6 +40,7 @@
|
|||||||
"@graphql-codegen/typescript-operations": "^1.17.8",
|
"@graphql-codegen/typescript-operations": "^1.17.8",
|
||||||
"@types/cookie": "^0.4.0",
|
"@types/cookie": "^0.4.0",
|
||||||
"@types/js-cookie": "^2.2.6",
|
"@types/js-cookie": "^2.2.6",
|
||||||
|
"@types/lodash.debounce": "^4.0.6",
|
||||||
"@types/node": "^14.11.2",
|
"@types/node": "^14.11.2",
|
||||||
"@types/react": "^16.9.49",
|
"@types/react": "^16.9.49",
|
||||||
"graphql": "^15.3.0",
|
"graphql": "^15.3.0",
|
||||||
|
17
yarn.lock
17
yarn.lock
@ -1572,6 +1572,18 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@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":
|
"@types/node@*", "@types/node@^14.11.2":
|
||||||
version "14.11.2"
|
version "14.11.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.2.tgz#2de1ed6670439387da1c9f549a2ade2b0a799256"
|
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"
|
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
|
||||||
integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=
|
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:
|
lodash.includes@^4.3.0:
|
||||||
version "4.3.0"
|
version "4.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user