diff --git a/src/use-swr.ts b/src/use-swr.ts index 4603f88a8..bfff2325e 100644 --- a/src/use-swr.ts +++ b/src/use-swr.ts @@ -35,7 +35,7 @@ const WITH_DEDUPE = { dedupe: true } export const useSWRHandler = ( _key: Key, - fn: Fetcher | null, + fetcher: Fetcher | null, config: typeof defaultConfig & SWRConfiguration ) => { const { @@ -74,6 +74,7 @@ export const useSWRHandler = ( // Refs to keep the key and config. const keyRef = useRef(key) + const fetcherRef = useRef(fetcher) const configRef = useRef(config) const getConfig = () => configRef.current @@ -106,7 +107,7 @@ export const useSWRHandler = ( // Resolve the current validating state. const resolveValidating = () => { - if (!key || !fn) return false + if (!key || !fetcher) return false if (cache.get(keyValidating)) return true // If it's not mounted yet and it should revalidate on mount, revalidate. @@ -127,7 +128,14 @@ export const useSWRHandler = ( // `fetcher`, to correctly handle the many edge cases. const revalidate = useCallback( async (revalidateOpts?: RevalidatorOptions): Promise => { - if (!key || !fn || unmountedRef.current || getConfig().isPaused()) { + const currentFetcher = fetcherRef.current + + if ( + !key || + !currentFetcher || + unmountedRef.current || + getConfig().isPaused() + ) { return false } @@ -196,7 +204,7 @@ export const useSWRHandler = ( // Start the request and keep the timestamp. CONCURRENT_PROMISES_TS[key] = getTimestamp() - CONCURRENT_PROMISES[key] = fn(...fnArgs) + CONCURRENT_PROMISES[key] = currentFetcher(...fnArgs) } // Wait until the ongoing request is done. Deduplication is also @@ -345,8 +353,9 @@ export const useSWRHandler = ( [] ) - // Always update config. + // Always update fetcher and config refs. useIsomorphicLayoutEffect(() => { + fetcherRef.current = fetcher configRef.current = config }) diff --git a/test/use-swr-fetcher.test.tsx b/test/use-swr-fetcher.test.tsx new file mode 100644 index 000000000..9ff6ad2bd --- /dev/null +++ b/test/use-swr-fetcher.test.tsx @@ -0,0 +1,36 @@ +import { act, screen } from '@testing-library/react' +import React, { useState } from 'react' +import useSWR from 'swr' +import { createKey, renderWithConfig, nextTick } from './utils' + +describe('useSWR - fetcher', () => { + // https://github.com/vercel/swr/issues/1131 + it('should use the latest fetcher reference', async () => { + const key = createKey() + let fetcher = () => 'foo' + let mutate + let rerender + + function Page() { + const { data, mutate: boundMutate } = useSWR(key, fetcher) + rerender = useState({})[1] + mutate = boundMutate + + return
data:{data}
+ } + + renderWithConfig() + await nextTick() + screen.getByText('data:foo') + + // Change the fetcher and make sure the ref is updated. + fetcher = () => 'bar' + act(() => rerender({})) + + // Revalidate. + await act(() => mutate()) + + // Should fetch with the new fetcher. + await screen.findByText('data:bar') + }) +})