@@ -87,10 +87,10 @@ export const useSWRHandler = <Data = any, Error = any>(
87
87
const getConfig = ( ) => configRef . current
88
88
const isActive = ( ) => getConfig ( ) . isVisible ( ) && getConfig ( ) . isOnline ( )
89
89
90
- const [ get , set ] = createCacheHelper < Data > ( cache , key )
90
+ const [ getCache , setCache ] = createCacheHelper < Data > ( cache , key )
91
91
92
92
// Get the current state that SWR should return.
93
- const cached = get ( )
93
+ const cached = getCache ( )
94
94
const cachedData = cached . data
95
95
const fallback = isUndefined ( fallbackData )
96
96
? config . fallback [ key ]
@@ -103,7 +103,7 @@ export const useSWRHandler = <Data = any, Error = any>(
103
103
// - Suspense mode and there's stale data for the initial render.
104
104
// - Not suspense mode and there is no fallback data and `revalidateIfStale` is enabled.
105
105
// - `revalidateIfStale` is enabled but `data` is not defined.
106
- const shouldRevalidate = ( ) => {
106
+ const shouldDoInitialRevalidation = ( ( ) => {
107
107
// If `revalidateOnMount` is set, we take the value directly.
108
108
if ( isInitialMount && ! isUndefined ( revalidateOnMount ) )
109
109
return revalidateOnMount
@@ -119,22 +119,24 @@ export const useSWRHandler = <Data = any, Error = any>(
119
119
// If there is no stale data, we need to revalidate on mount;
120
120
// If `revalidateIfStale` is set to true, we will always revalidate.
121
121
return isUndefined ( data ) || config . revalidateIfStale
122
- }
123
-
124
- // Resolve the current validating state.
125
- const resolveValidating = ( ) => {
126
- if ( ! key || ! fetcher ) return false
127
- if ( cached . isValidating ) return true
128
-
129
- // If it's not mounted yet and it should revalidate on mount, revalidate.
130
- return isInitialMount && shouldRevalidate ( )
131
- }
132
- const isValidating = resolveValidating ( )
122
+ } ) ( )
123
+
124
+ // Resolve the default validating state:
125
+ // If it's able to validate, and it should revalidate on mount, this will be true.
126
+ const defaultValidatingState = ! ! (
127
+ key &&
128
+ fetcher &&
129
+ isInitialMount &&
130
+ shouldDoInitialRevalidation
131
+ )
132
+ const isValidating = cached . isValidating || defaultValidatingState
133
+ const isLoading = cached . isLoading || defaultValidatingState
133
134
134
135
const currentState = {
135
136
data,
136
137
error,
137
- isValidating
138
+ isValidating,
139
+ isLoading
138
140
}
139
141
const [ stateRef , stateDependencies , setState ] = useStateWithDeps ( currentState )
140
142
@@ -171,6 +173,18 @@ export const useSWRHandler = <Data = any, Error = any>(
171
173
key === keyRef . current &&
172
174
initialMountedRef . current
173
175
176
+ // The final state object when request finishes.
177
+ const finalState : State < Data , Error > = {
178
+ isValidating : false ,
179
+ isLoading : false
180
+ }
181
+ const finishRequestAndUpdateState = ( ) => {
182
+ setCache ( finalState )
183
+ // We can only set state if it's safe (still mounted with the same key).
184
+ if ( isCurrentKeyMounted ( ) ) {
185
+ setState ( finalState )
186
+ }
187
+ }
174
188
const cleanupState = ( ) => {
175
189
// Check if it's still the same request before deleting.
176
190
const requestInfo = FETCH [ key ]
@@ -179,19 +193,15 @@ export const useSWRHandler = <Data = any, Error = any>(
179
193
}
180
194
}
181
195
182
- // The new state object when request finishes.
183
- const newState : State < Data , Error > = { isValidating : false }
184
- const finishRequestAndUpdateState = ( ) => {
185
- set ( { isValidating : false } )
186
- // We can only set state if it's safe (still mounted with the same key).
187
- if ( isCurrentKeyMounted ( ) ) {
188
- setState ( newState )
189
- }
190
- }
191
-
192
196
// Start fetching. Change the `isValidating` state, update the cache.
193
- set ( { isValidating : true } )
194
- setState ( { isValidating : true } )
197
+ const initialState : State < Data , Error > = { isValidating : true }
198
+ // It is in the `isLoading` state, if and only if there is no cached data.
199
+ // This bypasses fallback data and laggy data.
200
+ if ( isUndefined ( getCache ( ) . data ) ) {
201
+ initialState . isLoading = true
202
+ }
203
+ setCache ( initialState )
204
+ setState ( initialState )
195
205
196
206
try {
197
207
if ( shouldStartNewRequest ) {
@@ -203,7 +213,7 @@ export const useSWRHandler = <Data = any, Error = any>(
203
213
204
214
// If no cache being rendered currently (it shows a blank page),
205
215
// we trigger the loading slow event.
206
- if ( config . loadingTimeout && isUndefined ( get ( ) . data ) ) {
216
+ if ( config . loadingTimeout && isUndefined ( getCache ( ) . data ) ) {
207
217
setTimeout ( ( ) => {
208
218
if ( loading && isCurrentKeyMounted ( ) ) {
209
219
getConfig ( ) . onLoadingSlow ( key , config )
@@ -246,8 +256,7 @@ export const useSWRHandler = <Data = any, Error = any>(
246
256
}
247
257
248
258
// Clear error.
249
- set ( { error : UNDEFINED } )
250
- newState . error = UNDEFINED
259
+ finalState . error = UNDEFINED
251
260
252
261
// If there're other mutations(s), overlapped with the current revalidation:
253
262
// case 1:
@@ -283,19 +292,13 @@ export const useSWRHandler = <Data = any, Error = any>(
283
292
// Deep compare with latest state to avoid extra re-renders.
284
293
// For local state, compare and assign.
285
294
if ( ! compare ( stateRef . current . data , newData ) ) {
286
- newState . data = newData
295
+ finalState . data = newData
287
296
} else {
288
- // data and newData are deeply equal
289
- // it should be safe to broadcast the stale data
290
- newState . data = stateRef . current . data
297
+ // ` data` and ` newData` are deeply equal (serialized value).
298
+ // So it should be safe to broadcast the stale data to keep referential equality (===).
299
+ finalState . data = stateRef . current . data
291
300
// At the end of this function, `broadcastState` invokes the `onStateUpdate` function,
292
- // which takes care of avoiding the re-render
293
- }
294
-
295
- // For global state, it's possible that the key has changed.
296
- // https://github.com/vercel/swr/pull/1058
297
- if ( ! compare ( get ( ) . data , newData ) ) {
298
- set ( { data : newData } )
301
+ // which takes care of avoiding the re-render.
299
302
}
300
303
301
304
// Trigger the successful callback if it's the original request.
@@ -313,8 +316,7 @@ export const useSWRHandler = <Data = any, Error = any>(
313
316
// Not paused, we continue handling the error. Otherwise discard it.
314
317
if ( ! currentConfig . isPaused ( ) ) {
315
318
// Get a new error, don't use deep comparison for errors.
316
- set ( { error : err } )
317
- newState . error = err as Error
319
+ finalState . error = err as Error
318
320
319
321
// Error event and retry logic. Only for the actual request, not
320
322
// deduped ones.
@@ -354,7 +356,7 @@ export const useSWRHandler = <Data = any, Error = any>(
354
356
// Here is the source of the request, need to tell all other hooks to
355
357
// update their states.
356
358
if ( isCurrentKeyMounted ( ) && shouldStartNewRequest ) {
357
- broadcastState ( cache , key , { ... newState , isValidating : false } )
359
+ broadcastState ( cache , key , finalState )
358
360
}
359
361
360
362
return true
@@ -396,7 +398,6 @@ export const useSWRHandler = <Data = any, Error = any>(
396
398
useIsomorphicLayoutEffect ( ( ) => {
397
399
if ( ! key ) return
398
400
399
- const keyChanged = key !== keyRef . current
400
401
const softRevalidate = revalidate . bind ( UNDEFINED , WITH_DEDUPE )
401
402
402
403
// Expose state updater to global event listeners. So we can update hook's
@@ -451,18 +452,8 @@ export const useSWRHandler = <Data = any, Error = any>(
451
452
keyRef . current = key
452
453
initialMountedRef . current = true
453
454
454
- // When `key` updates, reset the state to the initial value
455
- // and trigger a rerender if necessary.
456
- if ( keyChanged ) {
457
- setState ( {
458
- data,
459
- error,
460
- isValidating
461
- } )
462
- }
463
-
464
455
// Trigger a revalidation.
465
- if ( shouldRevalidate ( ) ) {
456
+ if ( shouldDoInitialRevalidation ) {
466
457
if ( isUndefined ( data ) || IS_SERVER ) {
467
458
// Revalidate immediately.
468
459
softRevalidate ( )
@@ -480,7 +471,7 @@ export const useSWRHandler = <Data = any, Error = any>(
480
471
unsubUpdate ( )
481
472
unsubEvents ( )
482
473
}
483
- } , [ key , revalidate ] )
474
+ } , [ key ] )
484
475
485
476
// Polling
486
477
useIsomorphicLayoutEffect ( ( ) => {
@@ -524,7 +515,7 @@ export const useSWRHandler = <Data = any, Error = any>(
524
515
timer = - 1
525
516
}
526
517
}
527
- } , [ refreshInterval , refreshWhenHidden , refreshWhenOffline , revalidate ] )
518
+ } , [ refreshInterval , refreshWhenHidden , refreshWhenOffline , key ] )
528
519
529
520
// Display debug info in React DevTools.
530
521
useDebugValue ( data )
@@ -555,11 +546,9 @@ export const useSWRHandler = <Data = any, Error = any>(
555
546
stateDependencies . isValidating = true
556
547
return isValidating
557
548
} ,
558
- get isFallback ( ) {
559
- stateDependencies . data = true
560
- // `isFallback` is only true when we are displaying a value other than
561
- // the cached one.
562
- return data !== cachedData
549
+ get isLoading ( ) {
550
+ stateDependencies . isLoading = true
551
+ return isLoading
563
552
}
564
553
} as SWRResponse < Data , Error >
565
554
}
0 commit comments