diff --git a/src/_internal/types.ts b/src/_internal/types.ts index 528dae932..4e46049e8 100644 --- a/src/_internal/types.ts +++ b/src/_internal/types.ts @@ -116,6 +116,11 @@ export interface PublicConfiguration< * @link https://swr.vercel.app/docs/revalidation#disable-automatic-revalidations */ revalidateIfStale: boolean + /** + * automatically revalidate when data comes from RSC fallback + * @defaultValue false + */ + revalidateOnRSCFallback?: boolean /** * retry when fetcher has an error * @defaultValue true diff --git a/src/_internal/utils/config.ts b/src/_internal/utils/config.ts index ce05caea2..986cc6e4a 100644 --- a/src/_internal/utils/config.ts +++ b/src/_internal/utils/config.ts @@ -60,6 +60,7 @@ export const defaultConfig: FullConfiguration = mergeObjects( revalidateOnReconnect: true, revalidateIfStale: true, shouldRetryOnError: true, + revalidateOnRSCFallback: false, // timeouts errorRetryInterval: slowConnection ? 10000 : 5000, diff --git a/src/index/use-swr.ts b/src/index/use-swr.ts index 21c42a9c2..c91cc3156 100644 --- a/src/index/use-swr.ts +++ b/src/index/use-swr.ts @@ -299,6 +299,9 @@ export const useSWRHandler = ( // If it's paused, we skip revalidation. if (getConfig().isPaused()) return false + // If RSC fallback data on initial render, use revalidateOnRSCFallback config + if (!isUndefined(fallback) && !isUndefined(data) && !IS_SERVER && isInitialMount) return getConfig().revalidateOnRSCFallback ?? false; + // Under suspense mode, it will always fetch on render if there is no // stale data so no need to revalidate immediately mount it again. // If data exists, only revalidate if `revalidateIfStale` is true. diff --git a/test/rsc-fallback.test.tsx b/test/rsc-fallback.test.tsx new file mode 100644 index 000000000..3a6564f7c --- /dev/null +++ b/test/rsc-fallback.test.tsx @@ -0,0 +1,47 @@ +import { act, fireEvent, render, screen } from '@testing-library/react' +import React from 'react' +import useSWR, { SWRConfig } from 'swr' +import { sleep } from './utils' + +describe('RSC Fallback', () => { + it('should not revalidate on mount with RSC fallback data by default', async () => { + const fetchFn = jest.fn(() => 'updated data') + + function Page() { + const { data } = useSWR('key', fetchFn, { + fallbackData: 'RSC data' + }) + return
Data: {data}
+ } + + render() + + expect(screen.getByText('Data: RSC data')).toBeInTheDocument() + expect(fetchFn).not.toHaveBeenCalled() + + await sleep(50) + + expect(fetchFn).not.toHaveBeenCalled() + }) + + it('should revalidate on mount with RSC fallback data when revalidateOnRSCFallback is true', async () => { + const fetchFn = jest.fn(() => 'updated data') + + function Page() { + const { data } = useSWR('key', fetchFn, { + fallbackData: 'RSC data', + revalidateOnRSCFallback: true + }) + return
Data: {data}
+ } + + render() + + expect(screen.getByText('Data: RSC data')).toBeInTheDocument() + expect(fetchFn).toHaveBeenCalledTimes(1) + + await sleep(50) + + expect(screen.getByText('Data: updated data')).toBeInTheDocument() + }) +})