1- import { ReactNode , useCallback , useEffect , useMemo , useRef , useState } from 'react' ;
1+ import { DependencyList , ReactNode , useCallback , useEffect , useMemo , useRef , useState } from 'react' ;
22import LoadingPulse from '@/components/common/indicators/loading-pulse' ;
33import LoadingIndicator from '@/components/common/indicators/loading-indicator' ;
44import ErrorIndicator from '@/components/common/indicators/error-indicator' ;
@@ -37,8 +37,9 @@ export const useConfig: DataStateConstructor = <T, A extends any[], R, P>(config
3737 const setError = ( unknownError : unknown , prefix ?: string ) => setRoot ( DataRoot . error ( unknownError , prefix ) ) ;
3838
3939 // Stabilize args and default to empty array if no args provided:
40-
41- const args = useMemo ( ( ) => config . args || [ ] as unknown as A , config . args || [ ] as unknown as A ) ;
40+ const args = useMemo ( ( ) => config . args || [ ] as unknown as A ,
41+ // eslint-disable-next-line react-hooks/exhaustive-deps
42+ config . args || [ ] as unknown as A ) ;
4243
4344 // Stabilize fetcher: only create it once and don't update it.
4445 // Even if the parent re-creates the fetcher each render (e.g. when defined
@@ -123,11 +124,20 @@ export const useConfig: DataStateConstructor = <T, A extends any[], R, P>(config
123124 }
124125
125126 // Create a new DataState containing a subset of the fields of another:
126- const useSubset = < S , > ( selector : ( data : T ) => S ) : DataState < S > => {
127- const subsetPostProcess = ( response : R ) : S => {
127+ const useSubset = < S , B extends any [ ] > (
128+ selectorFn : ( data : T , ...args : B ) => S ,
129+ selectorArgs : B ,
130+ ) : DataState < S > => {
131+ // Stabilize the selector function once - it never changes:
132+ const stableSelector = useRef ( selectorFn ) . current ;
133+ // Stabilize the arguments:
134+ // eslint-disable-next-line react-hooks/exhaustive-deps
135+ const stableArgs = useMemo ( ( ) => selectorArgs , selectorArgs ) ;
136+
137+ const subsetPostProcess = useCallback ( ( response : R ) : S => {
128138 const result = ( postProcess ? postProcess ( response ) : response ) as unknown as T ;
129- return selector ( result ) ;
130- } ;
139+ return stableSelector ( result , ... stableArgs ) ;
140+ } , [ stableArgs , postProcess , stableSelector ] ) ;
131141
132142 const subsetConfig = {
133143 fetcher : fetcher ,
@@ -144,7 +154,7 @@ export const useConfig: DataStateConstructor = <T, A extends any[], R, P>(config
144154 break ;
145155 case 'value' :
146156 try {
147- const selectedValue = selector ( dataRoot . value ) ;
157+ const selectedValue = stableSelector ( dataRoot . value , ... stableArgs ) ;
148158 subsetData . setRoot ( DataRoot . value ( selectedValue ) ) ;
149159 } catch ( err ) {
150160 // Handle selector errors gracefully:
@@ -159,7 +169,8 @@ export const useConfig: DataStateConstructor = <T, A extends any[], R, P>(config
159169 subsetData . setRoot ( DataRoot . error ( dataRoot . error ) ) ;
160170 break ;
161171 }
162- } , [ dataRoot ] ) ;
172+ // eslint-disable-next-line react-hooks/exhaustive-deps
173+ } , [ dataRoot , stableArgs ] ) ;
163174
164175 return subsetData ;
165176 }
0 commit comments