From 74811667e5744a63edf46bf210c1f665bc81d883 Mon Sep 17 00:00:00 2001 From: edumudu Date: Thu, 8 Sep 2022 01:04:53 -0300 Subject: [PATCH] fix: skip dedup when call mutate --- lib/composables/swr/index.ts | 13 +++-- lib/composables/swr/swr-mutate.spec.ts | 59 ++++++++++++++++++++--- lib/composables/swr/swr.spec.ts | 67 -------------------------- lib/types/lib.ts | 4 ++ tsconfig.json | 1 - 5 files changed, 65 insertions(+), 79 deletions(-) diff --git a/lib/composables/swr/index.ts b/lib/composables/swr/index.ts index db7a9ab..36be4b7 100644 --- a/lib/composables/swr/index.ts +++ b/lib/composables/swr/index.ts @@ -4,6 +4,7 @@ import { createUnrefFn, toReactive, useEventListener, useIntervalFn } from '@vue import type { MaybeRef, OmitFirstArrayIndex, + RevalidatorOpts, SWRComposableConfig, SWRConfig, SWRFetcher, @@ -88,11 +89,16 @@ export const useSWR = ( const fetchedIn = useCachedRef(valueInCache.value?.fetchedIn ?? new Date(), { cache: cacheProvider, stateKey: 'fetchedIn', key }); /* eslint-enable */ - const fetchData = async () => { + const fetchData = async (opts: RevalidatorOpts = { dedup: true }) => { const timestampToDedupExpire = (fetchedIn.value?.getTime() || 0) + dedupingInterval; const hasNotExpired = timestampToDedupExpire > Date.now(); - if (hasCachedValue.value && (hasNotExpired || (isValidating.value && dedupingInterval !== 0))) + // Dedup requets + if ( + opts.dedup && + hasCachedValue.value && + (hasNotExpired || (isValidating.value && dedupingInterval !== 0)) + ) return; isValidating.value = true; @@ -140,7 +146,8 @@ export const useSWR = ( return; } - await fetchData(); + // Skip dedup when trigger by mutate + await fetchData({ dedup: false }); }; const onKeyChange = (newKey: string, oldKey?: string) => { diff --git a/lib/composables/swr/swr-mutate.spec.ts b/lib/composables/swr/swr-mutate.spec.ts index a69eef2..518b9d4 100644 --- a/lib/composables/swr/swr-mutate.spec.ts +++ b/lib/composables/swr/swr-mutate.spec.ts @@ -17,7 +17,7 @@ const setTimeoutPromise = (ms: number, resolveTo: unknown) => const useSWRWrapped: typeof useSWR = (...params) => { return useInjectedSetup( - () => configureGlobalSWR({ cacheProvider, dedupingInterval: 0 }), + () => configureGlobalSWR({ cacheProvider }), () => useSWR(...params), ); }; @@ -40,18 +40,23 @@ describe('useSWR - mutate', () => { vi.useRealTimers(); }); - it('should change local data variable value when mutate resolves', async () => { + it('should change local data variable value when binded mutate resolves', async () => { const { mutate, data } = useSWRWrapped(defaultKey, () => 'FetcherResult'); await nextTick(); expect(data.value).toEqual('FetcherResult'); await mutate(() => 'newValue', { revalidate: false }); - await nextTick(); expect(data.value).toEqual('newValue'); + + await mutate(Promise.resolve('promised value'), { revalidate: false }); + expect(data.value).toEqual('promised value'); + + await mutate(['raw value'], { revalidate: false }); + expect(data.value).toEqual(['raw value']); }); - it('should change local data variable value when mutate is called with `optimistcData`', async () => { + it('should change local data variable value when binded mutate is called with `optimistcData`', async () => { setDataToMockedCache(defaultKey, { data: 'cachedData' }); const { mutate, data } = useInjectedSetup( @@ -61,12 +66,15 @@ describe('useSWR - mutate', () => { expect(data.value).toEqual('cachedData'); - mutate(() => setTimeoutPromise(1000, 'newValue'), { optimisticData: 'optimistcData' }); + mutate(() => setTimeoutPromise(1000, 'newValue'), { + optimisticData: 'optimistcData', + revalidate: false, + }); await nextTick(); expect(data.value).toEqual('optimistcData'); }); - it('should update all hooks with the same key when call mutates', async () => { + it('should update all hooks with the same key when call binded mutate', async () => { setDataToMockedCache(defaultKey, { data: 'cachedData' }); const { datas, mutate, differentData } = useInjectedSetup( @@ -93,7 +101,8 @@ describe('useSWR - mutate', () => { 'cachedData', ]); - await mutate(() => 'mutated value'); + await mutate(() => 'mutated value', { revalidate: false }); + await nextTick(); expect(datas.map((data) => data.value)).toEqual([ 'mutated value', 'mutated value', @@ -112,7 +121,7 @@ describe('useSWR - mutate', () => { () => { const { mutate: localGlobalMutate } = useSWRConfig(); // eslint-disable-next-line no-plusplus - const swrResult = useSWR(defaultKey, () => value++, { dedupingInterval: 0 }); + const swrResult = useSWR(defaultKey, () => value++); return { globalMutate: localGlobalMutate, @@ -134,4 +143,38 @@ describe('useSWR - mutate', () => { await nextTick(); expect(data.value).toEqual(2); }); + + it('should ignore dedup interval when call binded mutate', async () => { + const fetcher = defaultFetcher; + const { mutate } = useSWRWrapped(defaultKey, fetcher, { dedupingInterval: 50000000 }); + + await nextTick(); + fetcher.mockReset(); + + await mutate(); + expect(fetcher).toHaveBeenCalledTimes(1); + + await mutate(); + expect(fetcher).toHaveBeenCalledTimes(2); + }); + + it('should revalidate when call binded mutate', async () => { + const fetcher = defaultFetcher; + const { mutate } = useSWRWrapped(defaultKey, fetcher, { dedupingInterval: 50000000 }); + + await nextTick(); + fetcher.mockReset(); + + await mutate(); + expect(fetcher).toHaveBeenCalledOnce(); + + await mutate(['new vakye']); + expect(fetcher).toHaveBeenCalledTimes(2); + + await mutate(() => 'new vakye'); + expect(fetcher).toHaveBeenCalledTimes(3); + + await mutate(Promise.resolve('promised value')); + expect(fetcher).toHaveBeenCalledTimes(4); + }); }); diff --git a/lib/composables/swr/swr.spec.ts b/lib/composables/swr/swr.spec.ts index f3430b7..07ddad8 100644 --- a/lib/composables/swr/swr.spec.ts +++ b/lib/composables/swr/swr.spec.ts @@ -277,73 +277,6 @@ describe('useSWR', () => { expect(fetcher).not.toHaveBeenCalled(); }); - it('should change local data variable value when mutate resolves', async () => { - setDataToMockedCache(defaultKey, { data: 'cachedData' }); - - const { mutate, data } = useInjectedSetup( - () => configureGlobalSWR({ cacheProvider }), - () => useSWR(defaultKey, () => 'FetcherResult'), - ); - - expect(data.value).toEqual('cachedData'); - await mutate(() => 'newValue'); - - expect(data.value).toEqual('newValue'); - }); - - it('should change local data variable value when mutate is called with `optimistcData`', async () => { - setDataToMockedCache(defaultKey, { data: 'cachedData' }); - - const { mutate, data } = useInjectedSetup( - () => configureGlobalSWR({ cacheProvider }), - () => useSWR(defaultKey, () => 'FetcherResult'), - ); - - expect(data.value).toEqual('cachedData'); - - mutate(() => setTimeoutPromise(1000, 'newValue'), { optimisticData: 'optimistcData' }); - await nextTick(); - expect(data.value).toEqual('optimistcData'); - }); - - it('should update all hooks with the same key when call mutates', async () => { - setDataToMockedCache(defaultKey, { data: 'cachedData' }); - - const { datas, mutate, differentData } = useInjectedSetup( - () => configureGlobalSWR({ cacheProvider }), - () => { - const { data: data1, mutate: localMutate } = useSWR(defaultKey, defaultFetcher); - const { data: data2 } = useSWR(defaultKey, defaultFetcher); - const { data: data3 } = useSWR(defaultKey, defaultFetcher); - const { data: data4 } = useSWR(defaultKey, defaultFetcher); - const { data: differentData1 } = useSWR('key-2', () => 'should not change'); - - return { - differentData: differentData1, - datas: [data1, data2, data3, data4], - mutate: localMutate, - }; - }, - ); - - expect(datas.map((data) => data.value)).toEqual([ - 'cachedData', - 'cachedData', - 'cachedData', - 'cachedData', - ]); - - await mutate(() => 'mutated value'); - expect(datas.map((data) => data.value)).toEqual([ - 'mutated value', - 'mutated value', - 'mutated value', - 'mutated value', - ]); - - expect(differentData.value).toEqual('should not change'); - }); - it('should not refresh if refreshInterval = 0', async () => { const fetcher = vi.fn(defaultFetcher); diff --git a/lib/types/lib.ts b/lib/types/lib.ts index 20eaab7..fe585c3 100644 --- a/lib/types/lib.ts +++ b/lib/types/lib.ts @@ -34,6 +34,10 @@ export type ScopeState = { revalidateCache: Map void | Promise>>; // callbacks to revalidate when key changes }; +export type RevalidatorOpts = { + dedup?: boolean; +}; + export type SWRConfig = { /** * stores the cached values diff --git a/tsconfig.json b/tsconfig.json index 2bd81a8..94a013a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,7 +23,6 @@ "types": ["vite/client", "vitest/globals"], "declarationDir": "./dist/types", "declaration": true, - "declarationMap": true }, "include": ["lib"] }