@@ -42,92 +42,98 @@ function generateCacheKey(request: Request): string {
42
42
if ( enableCache && enableFetchInstrumentation ) {
43
43
if ( typeof fetch === 'function' ) {
44
44
const originalFetch = fetch ;
45
- try {
46
- // eslint-disable-next-line no-native-reassign
47
- fetch = function fetch (
48
- resource : URL | RequestInfo ,
49
- options ?: RequestOptions ,
45
+ const cachedFetch = function fetch (
46
+ resource : URL | RequestInfo ,
47
+ options ?: RequestOptions ,
48
+ ) {
49
+ const dispatcher = ReactCurrentCache . current ;
50
+ if ( ! dispatcher ) {
51
+ // We're outside a cached scope.
52
+ return originalFetch ( resource , options ) ;
53
+ }
54
+ if (
55
+ options &&
56
+ options . signal &&
57
+ options . signal !== dispatcher . getCacheSignal ( )
50
58
) {
51
- const dispatcher = ReactCurrentCache . current ;
52
- if ( ! dispatcher ) {
53
- // We're outside a cached scope.
54
- return originalFetch ( resource , options ) ;
55
- }
59
+ // If we're passed a signal that is not ours, then we assume that
60
+ // someone else controls the lifetime of this object and opts out of
61
+ // caching. It's effectively the opt-out mechanism.
62
+ // Ideally we should be able to check this on the Request but
63
+ // it always gets initialized with its own signal so we don't
64
+ // know if it's supposed to override - unless we also override the
65
+ // Request constructor.
66
+ return originalFetch ( resource , options ) ;
67
+ }
68
+ // Normalize the Request
69
+ let url : string ;
70
+ let cacheKey : string ;
71
+ if ( typeof resource === 'string' && ! options ) {
72
+ // Fast path.
73
+ cacheKey = simpleCacheKey ;
74
+ url = resource ;
75
+ } else {
76
+ // Normalize the request.
77
+ const request = new Request ( resource , options ) ;
56
78
if (
57
- options &&
58
- options . signal &&
59
- options . signal !== dispatcher . getCacheSignal ( )
79
+ ( request . method !== 'GET' && request . method !== 'HEAD' ) ||
80
+ // $FlowFixMe: keepalive is real
81
+ request . keepalive
60
82
) {
61
- // If we're passed a signal that is not ours, then we assume that
62
- // someone else controls the lifetime of this object and opts out of
63
- // caching. It's effectively the opt-out mechanism.
64
- // Ideally we should be able to check this on the Request but
65
- // it always gets initialized with its own signal so we don't
66
- // know if it's supposed to override - unless we also override the
67
- // Request constructor.
83
+ // We currently don't dedupe requests that might have side-effects. Those
84
+ // have to be explicitly cached. We assume that the request doesn't have a
85
+ // body if it's GET or HEAD.
86
+ // keepalive gets treated the same as if you passed a custom cache signal.
68
87
return originalFetch ( resource , options ) ;
69
88
}
70
- // Normalize the Request
71
- let url : string ;
72
- let cacheKey : string ;
73
- if ( typeof resource === 'string' && ! options ) {
74
- // Fast path.
75
- cacheKey = simpleCacheKey ;
76
- url = resource ;
77
- } else {
78
- // Normalize the request.
79
- const request = new Request ( resource , options ) ;
80
- if (
81
- ( request . method !== 'GET' && request . method !== 'HEAD' ) ||
82
- // $FlowFixMe: keepalive is real
83
- request . keepalive
84
- ) {
85
- // We currently don't dedupe requests that might have side-effects. Those
86
- // have to be explicitly cached. We assume that the request doesn't have a
87
- // body if it's GET or HEAD.
88
- // keepalive gets treated the same as if you passed a custom cache signal.
89
- return originalFetch ( resource , options ) ;
89
+ cacheKey = generateCacheKey ( request ) ;
90
+ url = request . url ;
91
+ }
92
+ const cache = dispatcher . getCacheForType ( createFetchCache ) ;
93
+ const cacheEntries = cache . get ( url ) ;
94
+ let match ;
95
+ if ( cacheEntries === undefined ) {
96
+ // We pass the original arguments here in case normalizing the Request
97
+ // doesn't include all the options in this environment.
98
+ match = originalFetch ( resource , options ) ;
99
+ cache . set ( url , [ cacheKey , match ] ) ;
100
+ } else {
101
+ // We use an array as the inner data structure since it's lighter and
102
+ // we typically only expect to see one or two entries here.
103
+ for ( let i = 0 , l = cacheEntries . length ; i < l ; i += 2 ) {
104
+ const key = cacheEntries [ i ] ;
105
+ const value = cacheEntries [ i + 1 ] ;
106
+ if ( key === cacheKey ) {
107
+ match = value ;
108
+ // I would've preferred a labelled break but lint says no.
109
+ return match . then ( response => response . clone ( ) ) ;
90
110
}
91
- cacheKey = generateCacheKey ( request ) ;
92
- url = request . url ;
93
111
}
94
- const cache = dispatcher . getCacheForType ( createFetchCache ) ;
95
- const cacheEntries = cache . get ( url ) ;
96
- let match ;
97
- if ( cacheEntries === undefined ) {
98
- // We pass the original arguments here in case normalizing the Request
99
- // doesn't include all the options in this environment.
100
- match = originalFetch ( resource , options ) ;
101
- cache . set ( url , [ cacheKey , match ] ) ;
102
- } else {
103
- // We use an array as the inner data structure since it's lighter and
104
- // we typically only expect to see one or two entries here.
105
- for ( let i = 0 , l = cacheEntries . length ; i < l ; i += 2 ) {
106
- const key = cacheEntries [ i ] ;
107
- const value = cacheEntries [ i + 1 ] ;
108
- if ( key === cacheKey ) {
109
- match = value ;
110
- // I would've preferred a labelled break but lint says no.
111
- return match . then ( response => response . clone ( ) ) ;
112
- }
113
- }
114
- match = originalFetch ( resource , options ) ;
115
- cacheEntries . push ( cacheKey , match ) ;
116
- }
117
- // We clone the response so that each time you call this you get a new read
118
- // of the body so that it can be read multiple times.
119
- return match . then ( response => response . clone ( ) ) ;
120
- } ;
121
- // We don't expect to see any extra properties on fetch but if there are any,
122
- // copy them over. Useful for extended fetch environments or mocks.
123
- Object . assign ( fetch , originalFetch ) ;
124
- } catch ( error ) {
125
- // Log even in production just to make sure this is seen if only prod is frozen.
126
- // eslint-disable-next-line react-internal/no-production-logging
127
- console . warn (
128
- 'React was unable to patch the fetch() function in this environment. ' +
129
- 'Suspensey APIs might not work correctly as a result.' ,
130
- ) ;
112
+ match = originalFetch ( resource , options ) ;
113
+ cacheEntries . push ( cacheKey , match ) ;
114
+ }
115
+ // We clone the response so that each time you call this you get a new read
116
+ // of the body so that it can be read multiple times.
117
+ return match . then ( response => response . clone ( ) ) ;
118
+ } ;
119
+ // We don't expect to see any extra properties on fetch but if there are any,
120
+ // copy them over. Useful for extended fetch environments or mocks.
121
+ Object . assign ( cachedFetch , originalFetch ) ;
122
+ try {
123
+ // eslint-disable-next-line no-native-reassign
124
+ fetch = cachedFetch ;
125
+ } catch ( error1 ) {
126
+ try {
127
+ // In case assigning it globally fails, try globalThis instead just in case it exists.
128
+ globalThis . fetch = cachedFetch ;
129
+ } catch ( error2 ) {
130
+ // Log even in production just to make sure this is seen if only prod is frozen.
131
+ // eslint-disable-next-line react-internal/no-production-logging
132
+ console . warn (
133
+ 'React was unable to patch the fetch() function in this environment. ' +
134
+ 'Suspensey APIs might not work correctly as a result.' ,
135
+ ) ;
136
+ }
131
137
}
132
138
}
133
139
}
0 commit comments