From 403e6dc8239c47f4e911b041bad6796df4385615 Mon Sep 17 00:00:00 2001 From: yusheng chen Date: Sun, 16 Mar 2025 19:15:41 +0800 Subject: [PATCH] perf: optimize `useSWRConfig` with `useMemo` to maintain stable reference --- src/_internal/utils/use-swr-config.ts | 9 ++++- test/use-swr-context-config.test.tsx | 53 ++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/_internal/utils/use-swr-config.ts b/src/_internal/utils/use-swr-config.ts index e6aa43421..6d67e3b70 100644 --- a/src/_internal/utils/use-swr-config.ts +++ b/src/_internal/utils/use-swr-config.ts @@ -1,9 +1,14 @@ -import { useContext } from 'react' +import { useContext, useMemo } from 'react' import { defaultConfig } from './config' import { SWRConfigContext } from './config-context' import { mergeObjects } from './shared' import type { FullConfiguration } from '../types' export const useSWRConfig = (): FullConfiguration => { - return mergeObjects(defaultConfig, useContext(SWRConfigContext)) + const parentConfig = useContext(SWRConfigContext) + const mergedConfig = useMemo( + () => mergeObjects(defaultConfig, parentConfig), + [parentConfig] + ) + return mergedConfig } diff --git a/test/use-swr-context-config.test.tsx b/test/use-swr-context-config.test.tsx index 705007e03..a62e23bd6 100644 --- a/test/use-swr-context-config.test.tsx +++ b/test/use-swr-context-config.test.tsx @@ -1,6 +1,12 @@ -import { act, screen } from '@testing-library/react' -import useSWR, { mutate } from 'swr' +import { act, render, screen } from '@testing-library/react' +import useSWR, { + mutate, + SWRConfig, + type SWRConfiguration, + useSWRConfig +} from 'swr' import { createKey, createResponse, renderWithGlobalCache } from './utils' +import { useCallback, useEffect, useState } from 'react' describe('useSWR - context configs', () => { it('mutate before mount should not block rerender', async () => { @@ -25,3 +31,46 @@ describe('useSWR - context configs', () => { await screen.findByText('data') }) }) + +describe('useSWRConfig hook maintains stable reference across re-renders', () => { + it('should maintain the same swrConfig reference when counter updates', () => { + const parentConfig: SWRConfiguration = { + revalidateOnMount: true, + revalidateIfStale: false, + revalidateOnFocus: false, + revalidateOnReconnect: false + } + const counterButtonText = 'counter + 1' + let useSWRConfigReferenceChangedTimes = 0 + function Page() { + return ( + + + + ) + } + function ChildComponent() { + const swrConfig = useSWRConfig() + const [, setCounter] = useState(0) + const counterAddOne = useCallback( + () => setCounter(prev => prev + 1), + [setCounter] + ) + useEffect(() => { + useSWRConfigReferenceChangedTimes += 1 + }, [swrConfig]) + return + } + render() + act(() => { + screen.getByText(counterButtonText).click() + }) + act(() => { + screen.getByText(counterButtonText).click() + }) + act(() => { + screen.getByText(counterButtonText).click() + }) + expect(useSWRConfigReferenceChangedTimes).toBe(1) + }) +})