1- import { useEffect , useCallback , useSyncExternalStore , useMemo } from 'react' ;
1+ import {
2+ useEffect ,
3+ useCallback ,
4+ useSyncExternalStore ,
5+ useMemo ,
6+ useRef ,
7+ } from 'react' ;
28import {
39 fetchf ,
410 subscribe ,
@@ -29,6 +35,11 @@ const DEFAULT_RESULT = {
2935 config : { } ,
3036 headers : { } ,
3137} ;
38+ const DEFAULT_REF = [ null , { } , null ] as [
39+ string | null ,
40+ RequestConfig ,
41+ string | null ,
42+ ] ;
3243
3344/**
3445 * High-performance React hook for fetching data with caching, deduplication, revalidation etc.
@@ -106,6 +117,9 @@ export function useFetcher<
106117 const dedupeTime = config . dedupeTime ?? DEFAULT_DEDUPE_TIME_MS ;
107118 const cacheTime = config . cacheTime || INFINITE_CACHE_TIME ;
108119
120+ const currentValuesRef = useRef ( DEFAULT_REF ) ;
121+ currentValuesRef . current = [ url , config , cacheKey ] ;
122+
109123 const shouldTriggerOnMount = useMemo (
110124 ( ) => ( config . immediate === undefined ? true : config . immediate ) ,
111125 [ config . immediate ] ,
@@ -168,16 +182,18 @@ export function useFetcher<
168182
169183 const refetch = useCallback (
170184 async ( forceRefresh = true ) => {
171- // Truthy check for forceRefresh to ensure it's a boolean. It is useful in onClick handlers so to avoid additional annonymous function calls.
172- const shouldRefresh = ! ! forceRefresh ;
185+ const [ currUrl , currConfig , currCacheKey ] = currentValuesRef . current ;
173186
174- if ( ! url ) {
187+ if ( ! currUrl ) {
175188 return Promise . resolve ( null ) ;
176189 }
177190
191+ // Truthy check for forceRefresh to ensure it's a boolean. It is useful in onClick handlers so to avoid additional annonymous function calls.
192+ const shouldRefresh = ! ! forceRefresh ;
193+
178194 // Fast path: check cache first if not forcing refresh
179- if ( ! shouldRefresh && cacheKey ) {
180- const cached = getCachedResponse ( cacheKey , cacheTime , config ) ;
195+ if ( ! shouldRefresh && currCacheKey ) {
196+ const cached = getCachedResponse ( currCacheKey , cacheTime , currConfig ) ;
181197
182198 if ( cached ) {
183199 return Promise . resolve ( cached ) ;
@@ -186,11 +202,11 @@ export function useFetcher<
186202
187203 // When manual refetch is triggered, we want to ensure that the cache is busted
188204 // This can be disabled by passing `refetch(false)`
189- const cacheBuster = shouldRefresh ? ( ) => true : config . cacheBuster ;
205+ const cacheBuster = shouldRefresh ? ( ) => true : currConfig . cacheBuster ;
190206
191- return await fetchf ( url , {
192- ...config ,
193- cacheKey,
207+ return await fetchf ( currUrl , {
208+ ...currConfig ,
209+ cacheKey : currCacheKey ,
194210 dedupeTime,
195211 cacheTime,
196212 cacheBuster,
@@ -199,14 +215,14 @@ export function useFetcher<
199215 cacheErrors : true ,
200216 } ) ;
201217 } ,
202- [ url , cacheKey , cacheTime , dedupeTime ] ,
218+ [ cacheTime , dedupeTime ] ,
203219 ) ;
204220
205221 useEffect ( ( ) => {
206222 // Load the initial data if not already cached and not currently fetching
207223 if (
208- url &&
209224 shouldTriggerOnMount &&
225+ currentValuesRef . current [ 0 ] &&
210226 ! state . data &&
211227 ! state . error &&
212228 ! state . isFetching
@@ -223,9 +239,14 @@ export function useFetcher<
223239 incrementRef ( cacheKey ) ;
224240
225241 return ( ) => {
226- decrementRef ( cacheKey , cacheTime , dedupeTime , url ) ;
242+ decrementRef (
243+ cacheKey ,
244+ cacheTime ,
245+ dedupeTime ,
246+ currentValuesRef . current [ 0 ] ,
247+ ) ;
227248 } ;
228- } , [ url , shouldTriggerOnMount , refetch , cacheKey , cacheTime ] ) ;
249+ } , [ shouldTriggerOnMount , cacheKey , cacheTime ] ) ;
229250
230251 const mutate = useCallback <
231252 UseFetcherResult <
0 commit comments