Skip to content

Commit 1f209dc

Browse files
DeJeuneclaude
andauthored
fix: memoize provider selectors to prevent infinite re-renders (#12550)
The normalizeProvider() calls inside useAppSelector callbacks were creating new object references on every render, causing infinite re-render loops in components that depend on provider objects. This fix uses createSelector from Redux Toolkit to properly memoize the normalized providers, preventing unnecessary re-renders in: - GithubCopilotSettings (useEffect depends on provider) - Painting pages (useAllProviders returns new array each render) Fixes the "Maximum update depth exceeded" error introduced in 8186d4f. Co-authored-by: Claude Opus 4.5 <[email protected]>
1 parent f84941f commit 1f209dc

File tree

1 file changed

+28
-18
lines changed

1 file changed

+28
-18
lines changed

src/renderer/src/hooks/useProvider.ts

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createSelector } from '@reduxjs/toolkit'
22
import { CHERRYAI_PROVIDER } from '@renderer/config/providers'
33
import { getDefaultProvider } from '@renderer/services/AssistantService'
4-
import { useAppDispatch, useAppSelector } from '@renderer/store'
4+
import { type RootState, useAppDispatch, useAppSelector } from '@renderer/store'
55
import {
66
addModel,
77
addProvider,
@@ -14,6 +14,7 @@ import {
1414
import type { Assistant, Model, Provider } from '@renderer/types'
1515
import { isSystemProvider } from '@renderer/types'
1616
import { withoutTrailingSlash } from '@renderer/utils/api'
17+
import { useMemo } from 'react'
1718

1819
import { useDefaultModel } from './useAssistant'
1920

@@ -28,13 +29,27 @@ function normalizeProvider<T extends Provider>(provider: T): T {
2829
}
2930
}
3031

31-
const selectEnabledProviders = createSelector(
32-
(state) => state.llm.providers,
33-
(providers) =>
34-
providers
35-
.map(normalizeProvider)
36-
.filter((p) => p.enabled)
37-
.concat(CHERRYAI_PROVIDER)
32+
const selectProviders = (state: RootState) => state.llm.providers
33+
34+
const selectEnabledProviders = createSelector(selectProviders, (providers) =>
35+
providers
36+
.map(normalizeProvider)
37+
.filter((p) => p.enabled)
38+
.concat(CHERRYAI_PROVIDER)
39+
)
40+
41+
const selectSystemProviders = createSelector(selectProviders, (providers) =>
42+
providers.filter((p) => isSystemProvider(p)).map(normalizeProvider)
43+
)
44+
45+
const selectUserProviders = createSelector(selectProviders, (providers) =>
46+
providers.filter((p) => !isSystemProvider(p)).map(normalizeProvider)
47+
)
48+
49+
const selectAllProviders = createSelector(selectProviders, (providers) => providers.map(normalizeProvider))
50+
51+
const selectAllProvidersWithCherryAI = createSelector(selectProviders, (providers) =>
52+
[...providers, CHERRYAI_PROVIDER].map(normalizeProvider)
3853
)
3954

4055
export function useProviders() {
@@ -51,25 +66,20 @@ export function useProviders() {
5166
}
5267

5368
export function useSystemProviders() {
54-
return useAppSelector((state) => state.llm.providers.filter((p) => isSystemProvider(p)).map(normalizeProvider))
69+
return useAppSelector(selectSystemProviders)
5570
}
5671

5772
export function useUserProviders() {
58-
return useAppSelector((state) => state.llm.providers.filter((p) => !isSystemProvider(p)).map(normalizeProvider))
73+
return useAppSelector(selectUserProviders)
5974
}
6075

6176
export function useAllProviders() {
62-
return useAppSelector((state) => state.llm.providers.map(normalizeProvider))
77+
return useAppSelector(selectAllProviders)
6378
}
6479

6580
export function useProvider(id: string) {
66-
const provider =
67-
useAppSelector((state) =>
68-
state.llm.providers
69-
.concat([CHERRYAI_PROVIDER])
70-
.map(normalizeProvider)
71-
.find((p) => p.id === id)
72-
) || getDefaultProvider()
81+
const allProviders = useAppSelector(selectAllProvidersWithCherryAI)
82+
const provider = useMemo(() => allProviders.find((p) => p.id === id) || getDefaultProvider(), [allProviders, id])
7383
const dispatch = useAppDispatch()
7484

7585
return {

0 commit comments

Comments
 (0)