react-on-rails-pro 17.0.0-rc.7 → 17.0.0-rc.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/RSCProvider.js +27 -12
- package/lib/RSCProviderCache.d.ts +2 -0
- package/lib/RSCProviderCache.js +9 -0
- package/package.json +2 -2
package/lib/RSCProvider.js
CHANGED
|
@@ -286,24 +286,31 @@ export const createRSCProvider = ({ getServerComponent, domNodeId, }) => {
|
|
|
286
286
|
// refetch or reload (#3929). Evict the rejected entry so the next
|
|
287
287
|
// same-key `getComponent` starts a fresh fetch.
|
|
288
288
|
//
|
|
289
|
-
// Deferred
|
|
290
|
-
// React's Suspense machinery observes the
|
|
291
|
-
// promise before the entry is removed; a
|
|
292
|
-
// with React's own queue and evict before
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
//
|
|
289
|
+
// Deferred past the first macrotask (`setTimeout(0)`, not
|
|
290
|
+
// `queueMicrotask`) so React's Suspense machinery observes the
|
|
291
|
+
// rejection on the cached promise before the entry is removed; a
|
|
292
|
+
// microtask could interleave with React's own queue and evict before
|
|
293
|
+
// Suspense reads it. The extra macrotask also lets the `.finally`
|
|
294
|
+
// below release the in-flight pin before the key becomes available for
|
|
295
|
+
// a fresh same-key `getComponent`; otherwise a repeated failing request
|
|
296
|
+
// can replace the rejected promise with a new pending promise before
|
|
297
|
+
// the user ErrorBoundary commits its fallback (#4522). Guarded on
|
|
298
|
+
// promise identity so a newer same-key promise (such as
|
|
299
|
+
// `refetchComponent`) installed during that window is not evicted by
|
|
300
|
+
// this stale failure.
|
|
298
301
|
//
|
|
299
302
|
// NOTE: payloads that RESOLVE with an `Error` value are intentionally
|
|
300
303
|
// left cached here; whether those should also retry is a separate
|
|
301
304
|
// `getServerComponent` contract decision tracked apart from #3929.
|
|
305
|
+
let payloadRejected = false;
|
|
302
306
|
const evictPromiseIfRejected = (error) => {
|
|
307
|
+
payloadRejected = true;
|
|
303
308
|
setTimeout(() => {
|
|
304
|
-
|
|
305
|
-
fetchRSCPromises.
|
|
306
|
-
|
|
309
|
+
setTimeout(() => {
|
|
310
|
+
if (fetchRSCPromises.get(key, false) === promise) {
|
|
311
|
+
fetchRSCPromises.deleteWithoutEvict(key);
|
|
312
|
+
}
|
|
313
|
+
}, 0);
|
|
307
314
|
}, 0);
|
|
308
315
|
throw error;
|
|
309
316
|
};
|
|
@@ -337,6 +344,14 @@ export const createRSCProvider = ({ getServerComponent, domNodeId, }) => {
|
|
|
337
344
|
// resolving mounted routes can evict early visible keys before their
|
|
338
345
|
// layout effects get to pin them as mounted.
|
|
339
346
|
setTimeout(() => {
|
|
347
|
+
if (payloadRejected) {
|
|
348
|
+
// The rejected entry is already scheduled for deletion in the
|
|
349
|
+
// next macrotask. Releasing its pin must not reconcile over-cap
|
|
350
|
+
// eviction against healthy entries during this short grace
|
|
351
|
+
// window.
|
|
352
|
+
fetchRSCPromises.unpinWithoutEvict(key);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
340
355
|
fetchRSCPromises.unpin(key);
|
|
341
356
|
}, 0);
|
|
342
357
|
});
|
|
@@ -96,6 +96,8 @@ export declare class BoundedLRU<V> {
|
|
|
96
96
|
deleteWithoutEvict(key: string, preservePins?: boolean): void;
|
|
97
97
|
deletePreservingPins(key: string): void;
|
|
98
98
|
unpin(key: string): void;
|
|
99
|
+
unpinWithoutEvict(key: string): void;
|
|
100
|
+
private releasePin;
|
|
99
101
|
/**
|
|
100
102
|
* Evict least-recently-used unpinned keys until the map is back within
|
|
101
103
|
* `maxEntries`. `preventEvictingKey`, when given, is treated as un-evictable
|
package/lib/RSCProviderCache.js
CHANGED
|
@@ -160,6 +160,12 @@ export class BoundedLRU {
|
|
|
160
160
|
this.deleteWithoutEvict(key, true);
|
|
161
161
|
}
|
|
162
162
|
unpin(key) {
|
|
163
|
+
this.releasePin(key, true);
|
|
164
|
+
}
|
|
165
|
+
unpinWithoutEvict(key) {
|
|
166
|
+
this.releasePin(key, false);
|
|
167
|
+
}
|
|
168
|
+
releasePin(key, reconcileEviction) {
|
|
163
169
|
this.assertNotEvicting('unpin');
|
|
164
170
|
const count = this.pins.get(key);
|
|
165
171
|
if (count === undefined) {
|
|
@@ -170,6 +176,9 @@ export class BoundedLRU {
|
|
|
170
176
|
return;
|
|
171
177
|
}
|
|
172
178
|
this.pins.delete(key);
|
|
179
|
+
if (!reconcileEviction) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
173
182
|
// The in-flight operation that held this pin just settled, so the key is
|
|
174
183
|
// now the most-recently-used. `get` promotes only when the key is still
|
|
175
184
|
// present; a key already deleted, e.g. by `restoreLastSuccessfulPromise`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-on-rails-pro",
|
|
3
|
-
"version": "17.0.0-rc.
|
|
3
|
+
"version": "17.0.0-rc.8",
|
|
4
4
|
"description": "React on Rails Pro package with React Server Components support",
|
|
5
5
|
"main": "lib/ReactOnRails.full.js",
|
|
6
6
|
"type": "module",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"./ServerComponentFetchError": "./lib/ServerComponentFetchError.js"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"react-on-rails": "17.0.0-rc.
|
|
70
|
+
"react-on-rails": "17.0.0-rc.8"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
73
|
"react": ">= 16",
|