From 933a80147e790fa20ec71f63501e8875db8a619f Mon Sep 17 00:00:00 2001 From: Sergey Kozyrenko Date: Fri, 10 Jul 2026 13:10:14 +0700 Subject: [PATCH] perf(context): memoize providers + system-settings context values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both provider components built their context `value` as a fresh object literal each render (providers-provider also re-sorted into a new array and recreated its setter), forcing every consumer to re-render on each provider render. Memoize the sorted array, the setter, and both value objects — the two non-memoized outliers among the app's context providers. No behavior change. Co-Authored-By: Claude Opus 4.8 --- frontend/src/providers/providers-provider.tsx | 21 +++++++++++-------- .../providers/system-settings-provider.tsx | 19 ++++++++--------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/frontend/src/providers/providers-provider.tsx b/frontend/src/providers/providers-provider.tsx index 9b981518..df025012 100644 --- a/frontend/src/providers/providers-provider.tsx +++ b/frontend/src/providers/providers-provider.tsx @@ -1,5 +1,5 @@ import { useQuery } from '@apollo/client/react'; -import { createContext, useContext, useEffect, useMemo, useState } from 'react'; +import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import type { Provider } from '@/models/provider'; @@ -28,7 +28,7 @@ export function ProvidersProvider({ children }: ProvidersProviderProps) { skip: !isAuthenticated(), }); - const providers = sortProviders(providersData?.providers || []); + const providers = useMemo(() => sortProviders(providersData?.providers || []), [providersData?.providers]); const [selectedProviderName, setSelectedProviderName] = useState(() => { return localStorage.getItem(SELECTED_PROVIDER_KEY); @@ -56,15 +56,18 @@ export function ProvidersProvider({ children }: ProvidersProviderProps) { } }, [selectedProvider]); - const setSelectedProvider = (provider: Provider) => { + const setSelectedProvider = useCallback((provider: Provider) => { setSelectedProviderName(provider.name); - }; + }, []); - const value = { - providers, - selectedProvider, - setSelectedProvider, - }; + const value = useMemo( + () => ({ + providers, + selectedProvider, + setSelectedProvider, + }), + [providers, selectedProvider, setSelectedProvider], + ); return {children}; } diff --git a/frontend/src/providers/system-settings-provider.tsx b/frontend/src/providers/system-settings-provider.tsx index b48aaf11..539946d9 100644 --- a/frontend/src/providers/system-settings-provider.tsx +++ b/frontend/src/providers/system-settings-provider.tsx @@ -1,7 +1,7 @@ import type { ReactNode } from 'react'; import { useQuery } from '@apollo/client/react'; -import { createContext, use } from 'react'; +import { createContext, use, useMemo } from 'react'; import type { SettingsFragmentFragment } from '@/graphql/types'; @@ -22,16 +22,15 @@ export function SystemSettingsProvider({ children }: { children: ReactNode }) { skip: !isAuthenticated(), }); - return ( - - {children} - + const value = useMemo( + () => ({ + isLoading: loading, + settings: settingsData?.settings ?? null, + }), + [loading, settingsData?.settings], ); + + return {children}; } export function useSystemSettings() {