[be] Remove unshipped experimental <Cache> element type (#28698)
Removes the `<Cache />` element type since we're going with a simpler caching strategy.
Joseph Savona committed
Apr 2, 2024 at 07:57 UTC
7319c61b18274ee7e7c20bd2e533b93c922d8fe0
19 files changed
-1643
packages/react-devtools-shared/src/__tests__/store-test.js
-2
@@ -1575,7 +1575,6 @@ describe('Store', () => {
1575
<FakeHigherOrderComponent />
1576
<MemoizedFakeHigherOrderComponent />
1577
<ForwardRefFakeHigherOrderComponent />
1578
- <React.unstable_Cache />
1578
<MemoizedFakeHigherOrderComponentWithDisplayNameOverride />
1579
<ForwardRefFakeHigherOrderComponentWithDisplayNameOverride />
1580
</React.Fragment>
@@ -1603,7 +1602,6 @@ describe('Store', () => {
1602
<Baz> [withFoo][withBar]
1603
<Baz> [Memo][withFoo][withBar]
1604
<Baz> [ForwardRef][withFoo][withBar]
1606
- <Cache>
1605
<memoRefOverride>
1606
<forwardRefOverride>
1607
`);
packages/react-devtools-shell/src/app/ElementTypes/index.js
-1
@@ -18,7 +18,6 @@ import {
18
Profiler,
19
StrictMode,
20
Suspense,
21
- unstable_Cache as Cache,
21
} from 'react';
22
23
const Context = createContext('abc');
packages/react-reconciler/src/ReactFiber.js
-20
@@ -28,7 +28,6 @@ import {
28
isHostSingletonType,
29
} from './ReactFiberConfig';
30
import {
31
- enableCache,
31
enableProfilerTimer,
32
enableScopeAPI,
33
enableLegacyHidden,
@@ -66,7 +65,6 @@ import {
65
ScopeComponent,
66
OffscreenComponent,
67
LegacyHiddenComponent,
69
- CacheComponent,
68
TracingMarkerComponent,
69
} from './ReactWorkTags';
70
import {OffscreenVisible} from './ReactFiberActivityComponent';
@@ -104,7 +102,6 @@ import {
102
REACT_SCOPE_TYPE,
103
REACT_OFFSCREEN_TYPE,
104
REACT_LEGACY_HIDDEN_TYPE,
107
- REACT_CACHE_TYPE,
105
REACT_TRACING_MARKER_TYPE,
106
} from 'shared/ReactSymbols';
107
import {TransitionTracingMarker} from './ReactFiberTracingMarkerComponent';
@@ -560,11 +557,6 @@ export function createFiberFromTypeAndProps(
557
return createFiberFromScope(type, pendingProps, mode, lanes, key);
558
}
559
// Fall through
563
- case REACT_CACHE_TYPE:
564
- if (enableCache) {
565
- return createFiberFromCache(pendingProps, mode, lanes, key);
566
- }
567
- // Fall through
560
case REACT_TRACING_MARKER_TYPE:
561
if (enableTransitionTracing) {
562
return createFiberFromTracingMarker(pendingProps, mode, lanes, key);
@@ -807,18 +799,6 @@ export function createFiberFromLegacyHidden(
799
return fiber;
800
}
801
810
-export function createFiberFromCache(
811
- pendingProps: any,
812
- mode: TypeOfMode,
813
- lanes: Lanes,
814
- key: null | string,
815
-): Fiber {
816
- const fiber = createFiber(CacheComponent, pendingProps, key, mode);
817
- fiber.elementType = REACT_CACHE_TYPE;
818
- fiber.lanes = lanes;
819
- return fiber;
820
-}
821
-
802
export function createFiberFromTracingMarker(
803
pendingProps: any,
804
mode: TypeOfMode,
packages/react-reconciler/src/__tests__/ReactCacheElement-test.js
deleted
-1597
@@ -1,1597 +0,0 @@
1
-let React;
2
-let ReactNoop;
3
-let Cache;
4
-let getCacheSignal;
5
-let getCacheForType;
6
-let Scheduler;
7
-let assertLog;
8
-let act;
9
-let Suspense;
10
-let Activity;
11
-let useCacheRefresh;
12
-let startTransition;
13
-let useState;
14
-
15
-let textCaches;
16
-let seededCache;
17
-
18
-describe('ReactCacheElement', () => {
19
- beforeEach(() => {
20
- jest.resetModules();
21
-
22
- React = require('react');
23
- ReactNoop = require('react-noop-renderer');
24
- Cache = React.unstable_Cache;
25
- Scheduler = require('scheduler');
26
- act = require('internal-test-utils').act;
27
- Suspense = React.Suspense;
28
- Activity = React.unstable_Activity;
29
- getCacheSignal = React.unstable_getCacheSignal;
30
- getCacheForType = React.unstable_getCacheForType;
31
- useCacheRefresh = React.unstable_useCacheRefresh;
32
- startTransition = React.startTransition;
33
- useState = React.useState;
34
-
35
- const InternalTestUtils = require('internal-test-utils');
36
- assertLog = InternalTestUtils.assertLog;
37
-
38
- textCaches = [];
39
- seededCache = null;
40
- });
41
-
42
- function createTextCache() {
43
- if (seededCache !== null) {
44
- // Trick to seed a cache before it exists.
45
- // TODO: Need a built-in API to seed data before the initial render (i.e.
46
- // not a refresh because nothing has mounted yet).
47
- const textCache = seededCache;
48
- seededCache = null;
49
- return textCache;
50
- }
51
-
52
- const data = new Map();
53
- const version = textCaches.length + 1;
54
- const textCache = {
55
- version,
56
- data,
57
- resolve(text) {
58
- const record = data.get(text);
59
- if (record === undefined) {
60
- const newRecord = {
61
- status: 'resolved',
62
- value: text,
63
- cleanupScheduled: false,
64
- };
65
- data.set(text, newRecord);
66
- } else if (record.status === 'pending') {
67
- record.value.resolve();
68
- }
69
- },
70
- reject(text, error) {
71
- const record = data.get(text);
72
- if (record === undefined) {
73
- const newRecord = {
74
- status: 'rejected',
75
- value: error,
76
- cleanupScheduled: false,
77
- };
78
- data.set(text, newRecord);
79
- } else if (record.status === 'pending') {
80
- record.value.reject();
81
- }
82
- },
83
- };
84
- textCaches.push(textCache);
85
- return textCache;
86
- }
87
-
88
- function readText(text) {
89
- const signal = getCacheSignal ? getCacheSignal() : null;
90
- const textCache = getCacheForType(createTextCache);
91
- const record = textCache.data.get(text);
92
- if (record !== undefined) {
93
- if (!record.cleanupScheduled) {
94
- // This record was seeded prior to the abort signal being available:
95
- // schedule a cleanup function for it.
96
- // TODO: Add ability to cleanup entries seeded w useCacheRefresh()
97
- record.cleanupScheduled = true;
98
- if (getCacheSignal) {
99
- signal.addEventListener('abort', () => {
100
- Scheduler.log(`Cache cleanup: ${text} [v${textCache.version}]`);
101
- });
102
- }
103
- }
104
- switch (record.status) {
105
- case 'pending':
106
- throw record.value;
107
- case 'rejected':
108
- throw record.value;
109
- case 'resolved':
110
- return textCache.version;
111
- }
112
- } else {
113
- Scheduler.log(`Cache miss! [${text}]`);
114
-
115
- let resolve;
116
- let reject;
117
- const thenable = new Promise((res, rej) => {
118
- resolve = res;
119
- reject = rej;
120
- }).then(
121
- value => {
122
- if (newRecord.status === 'pending') {
123
- newRecord.status = 'resolved';
124
- newRecord.value = value;
125
- }
126
- },
127
- error => {
128
- if (newRecord.status === 'pending') {
129
- newRecord.status = 'rejected';
130
- newRecord.value = error;
131
- }
132
- },
133
- );
134
- thenable.resolve = resolve;
135
- thenable.reject = reject;
136
-
137
- const newRecord = {
138
- status: 'pending',
139
- value: thenable,
140
- cleanupScheduled: true,
141
- };
142
- textCache.data.set(text, newRecord);
143
-
144
- if (getCacheSignal) {
145
- signal.addEventListener('abort', () => {
146
- Scheduler.log(`Cache cleanup: ${text} [v${textCache.version}]`);
147
- });
148
- }
149
- throw thenable;
150
- }
151
- }
152
-
153
- function Text({text}) {
154
- Scheduler.log(text);
155
- return text;
156
- }
157
-
158
- function AsyncText({text, showVersion}) {
159
- const version = readText(text);
160
- const fullText = showVersion ? `${text} [v${version}]` : text;
161
- Scheduler.log(fullText);
162
- return fullText;
163
- }
164
-
165
- function seedNextTextCache(text) {
166
- if (seededCache === null) {
167
- seededCache = createTextCache();
168
- }
169
- seededCache.resolve(text);
170
- }
171
-
172
- function resolveMostRecentTextCache(text) {
173
- if (textCaches.length === 0) {
174
- throw Error('Cache does not exist.');
175
- } else {
176
- // Resolve the most recently created cache. An older cache can by
177
- // resolved with `textCaches[index].resolve(text)`.
178
- textCaches[textCaches.length - 1].resolve(text);
179
- }
180
- }
181
-
182
- // @gate enableCacheElement
183
- test('render Cache component', async () => {
184
- const root = ReactNoop.createRoot();
185
- await act(() => {
186
- root.render(<Cache>Hi</Cache>);
187
- });
188
- expect(root).toMatchRenderedOutput('Hi');
189
- });
190
-
191
- // @gate enableCacheElement
192
- test('mount new data', async () => {
193
- const root = ReactNoop.createRoot();
194
- await act(() => {
195
- root.render(
196
- <Cache>
197
- <Suspense fallback={<Text text="Loading..." />}>
198
- <AsyncText text="A" />
199
- </Suspense>
200
- </Cache>,
201
- );
202
- });
203
- assertLog(['Cache miss! [A]', 'Loading...']);
204
- expect(root).toMatchRenderedOutput('Loading...');
205
-
206
- await act(() => {
207
- resolveMostRecentTextCache('A');
208
- });
209
- assertLog(['A']);
210
- expect(root).toMatchRenderedOutput('A');
211
-
212
- await act(() => {
213
- root.render('Bye');
214
- });
215
- // no cleanup: cache is still retained at the root
216
- assertLog([]);
217
- expect(root).toMatchRenderedOutput('Bye');
218
- });
219
-
220
- // @gate enableCacheElement
221
- test('root acts as implicit cache boundary', async () => {
222
- const root = ReactNoop.createRoot();
223
- await act(() => {
224
- root.render(
225
- <Suspense fallback={<Text text="Loading..." />}>
226
- <AsyncText text="A" />
227
- </Suspense>,
228
- );
229
- });
230
- assertLog(['Cache miss! [A]', 'Loading...']);
231
- expect(root).toMatchRenderedOutput('Loading...');
232
-
233
- await act(() => {
234
- resolveMostRecentTextCache('A');
235
- });
236
- assertLog(['A']);
237
- expect(root).toMatchRenderedOutput('A');
238
-
239
- await act(() => {
240
- root.render('Bye');
241
- });
242
- // no cleanup: cache is still retained at the root
243
- assertLog([]);
244
- expect(root).toMatchRenderedOutput('Bye');
245
- });
246
-
247
- // @gate enableCacheElement
248
- test('multiple new Cache boundaries in the same mount share the same, fresh root cache', async () => {
249
- function App() {
250
- return (
251
- <>
252
- <Cache>
253
- <Suspense fallback={<Text text="Loading..." />}>
254
- <AsyncText text="A" />
255
- </Suspense>
256
- </Cache>
257
- <Cache>
258
- <Suspense fallback={<Text text="Loading..." />}>
259
- <AsyncText text="A" />
260
- </Suspense>
261
- </Cache>
262
- </>
263
- );
264
- }
265
-
266
- const root = ReactNoop.createRoot();
267
- await act(() => {
268
- root.render(<App showMore={false} />);
269
- });
270
-
271
- // Even though there are two new <Cache /> trees, they should share the same
272
- // data cache. So there should be only a single cache miss for A.
273
- assertLog(['Cache miss! [A]', 'Loading...', 'Loading...']);
274
- expect(root).toMatchRenderedOutput('Loading...Loading...');
275
-
276
- await act(() => {
277
- resolveMostRecentTextCache('A');
278
- });
279
- assertLog(['A', 'A']);
280
- expect(root).toMatchRenderedOutput('AA');
281
-
282
- await act(() => {
283
- root.render('Bye');
284
- });
285
- // no cleanup: cache is still retained at the root
286
- assertLog([]);
287
- expect(root).toMatchRenderedOutput('Bye');
288
- });
289
-
290
- // @gate enableCacheElement
291
- test('multiple new Cache boundaries in the same update share the same, fresh cache', async () => {
292
- function App({showMore}) {
293
- return showMore ? (
294
- <>
295
- <Cache>
296
- <Suspense fallback={<Text text="Loading..." />}>
297
- <AsyncText text="A" />
298
- </Suspense>
299
- </Cache>
300
- <Cache>
301
- <Suspense fallback={<Text text="Loading..." />}>
302
- <AsyncText text="A" />
303
- </Suspense>
304
- </Cache>
305
- </>
306
- ) : (
307
- '(empty)'
308
- );
309
- }
310
-
311
- const root = ReactNoop.createRoot();
312
- await act(() => {
313
- root.render(<App showMore={false} />);
314
- });
315
- assertLog([]);
316
- expect(root).toMatchRenderedOutput('(empty)');
317
-
318
- await act(() => {
319
- root.render(<App showMore={true} />);
320
- });
321
- // Even though there are two new <Cache /> trees, they should share the same
322
- // data cache. So there should be only a single cache miss for A.
323
- assertLog(['Cache miss! [A]', 'Loading...', 'Loading...']);
324
- expect(root).toMatchRenderedOutput('Loading...Loading...');
325
-
326
- await act(() => {
327
- resolveMostRecentTextCache('A');
328
- });
329
- assertLog(['A', 'A']);
330
- expect(root).toMatchRenderedOutput('AA');
331
-
332
- await act(() => {
333
- root.render('Bye');
334
- });
335
- // cleanup occurs for the cache shared by the inner cache boundaries (which
336
- // are not shared w the root because they were added in an update)
337
- // note that no cache is created for the root since the cache is never accessed
338
- assertLog(['Cache cleanup: A [v1]']);
339
- expect(root).toMatchRenderedOutput('Bye');
340
- });
341
-
342
- // @gate enableCacheElement
343
- test(
344
- 'nested cache boundaries share the same cache as the root during ' +
345
- 'the initial render',
346
- async () => {
347
- function App() {
348
- return (
349
- <Suspense fallback={<Text text="Loading..." />}>
350
- <AsyncText text="A" />
351
- <Cache>
352
- <AsyncText text="A" />
353
- </Cache>
354
- </Suspense>
355
- );
356
- }
357
-
358
- const root = ReactNoop.createRoot();
359
- await act(() => {
360
- root.render(<App />);
361
- });
362
- // Even though there is a nested <Cache /> boundary, it should share the same
363
- // data cache as the root. So there should be only a single cache miss for A.
364
- assertLog(['Cache miss! [A]', 'Loading...']);
365
- expect(root).toMatchRenderedOutput('Loading...');
366
-
367
- await act(() => {
368
- resolveMostRecentTextCache('A');
369
- });
370
- assertLog(['A', 'A']);
371
- expect(root).toMatchRenderedOutput('AA');
372
-
373
- await act(() => {
374
- root.render('Bye');
375
- });
376
- // no cleanup: cache is still retained at the root
377
- assertLog([]);
378
- expect(root).toMatchRenderedOutput('Bye');
379
- },
380
- );
381
-
382
- // @gate enableCacheElement
383
- test('new content inside an existing Cache boundary should re-use already cached data', async () => {
384
- function App({showMore}) {
385
- return (
386
- <Cache>
387
- <Suspense fallback={<Text text="Loading..." />}>
388
- <AsyncText showVersion={true} text="A" />
389
- </Suspense>
390
- {showMore ? (
391
- <Suspense fallback={<Text text="Loading..." />}>
392
- <AsyncText showVersion={true} text="A" />
393
- </Suspense>
394
- ) : null}
395
- </Cache>
396
- );
397
- }
398
-
399
- const root = ReactNoop.createRoot();
400
- await act(() => {
401
- seedNextTextCache('A');
402
- root.render(<App showMore={false} />);
403
- });
404
- assertLog(['A [v1]']);
405
- expect(root).toMatchRenderedOutput('A [v1]');
406
-
407
- // Add a new cache boundary
408
- await act(() => {
409
- root.render(<App showMore={true} />);
410
- });
411
- assertLog([
412
- 'A [v1]',
413
- // New tree should use already cached data
414
- 'A [v1]',
415
- ]);
416
- expect(root).toMatchRenderedOutput('A [v1]A [v1]');
417
-
418
- await act(() => {
419
- root.render('Bye');
420
- });
421
- // no cleanup: cache is still retained at the root
422
- assertLog([]);
423
- expect(root).toMatchRenderedOutput('Bye');
424
- });
425
-
426
- // @gate enableCacheElement
427
- test('a new Cache boundary uses fresh cache', async () => {
428
- // The only difference from the previous test is that the "Show More"
429
- // content is wrapped in a nested <Cache /> boundary
430
- function App({showMore}) {
431
- return (
432
- <Cache>
433
- <Suspense fallback={<Text text="Loading..." />}>
434
- <AsyncText showVersion={true} text="A" />
435
- </Suspense>
436
- {showMore ? (
437
- <Cache>
438
- <Suspense fallback={<Text text="Loading..." />}>
439
- <AsyncText showVersion={true} text="A" />
440
- </Suspense>
441
- </Cache>
442
- ) : null}
443
- </Cache>
444
- );
445
- }
446
-
447
- const root = ReactNoop.createRoot();
448
- await act(() => {
449
- seedNextTextCache('A');
450
- root.render(<App showMore={false} />);
451
- });
452
- assertLog(['A [v1]']);
453
- expect(root).toMatchRenderedOutput('A [v1]');
454
-
455
- // Add a new cache boundary
456
- await act(() => {
457
- root.render(<App showMore={true} />);
458
- });
459
- assertLog([
460
- 'A [v1]',
461
- // New tree should load fresh data.
462
- 'Cache miss! [A]',
463
- 'Loading...',
464
- ]);
465
- expect(root).toMatchRenderedOutput('A [v1]Loading...');
466
- await act(() => {
467
- resolveMostRecentTextCache('A');
468
- });
469
- assertLog(['A [v2]']);
470
- expect(root).toMatchRenderedOutput('A [v1]A [v2]');
471
-
472
- // Replace all the children: this should retain the root Cache instance,
473
- // but cleanup the separate cache instance created for the fresh cache
474
- // boundary
475
- await act(() => {
476
- root.render('Bye!');
477
- });
478
- // Cleanup occurs for the *second* cache instance: the first is still
479
- // referenced by the root
480
- assertLog(['Cache cleanup: A [v2]']);
481
- expect(root).toMatchRenderedOutput('Bye!');
482
- });
483
-
484
- // @gate enableCacheElement
485
- test('inner/outer cache boundaries uses the same cache instance on initial render', async () => {
486
- const root = ReactNoop.createRoot();
487
-
488
- function App() {
489
- return (
490
- <Cache>
491
- <Suspense fallback={<Text text="Loading shell..." />}>
492
- {/* The shell reads A */}
493
- <Shell>
494
- {/* The inner content reads both A and B */}
495
- <Suspense fallback={<Text text="Loading content..." />}>
496
- <Cache>
497
- <Content />
498
- </Cache>
499
- </Suspense>
500
- </Shell>
501
- </Suspense>
502
- </Cache>
503
- );
504
- }
505
-
506
- function Shell({children}) {
507
- readText('A');
508
- return (
509
- <>
510
- <div>
511
- <Text text="Shell" />
512
- </div>
513
- <div>{children}</div>
514
- </>
515
- );
516
- }
517
-
518
- function Content() {
519
- readText('A');
520
- readText('B');
521
- return <Text text="Content" />;
522
- }
523
-
524
- await act(() => {
525
- root.render(<App />);
526
- });
527
- assertLog(['Cache miss! [A]', 'Loading shell...']);
528
- expect(root).toMatchRenderedOutput('Loading shell...');
529
-
530
- await act(() => {
531
- resolveMostRecentTextCache('A');
532
- });
533
- assertLog([
534
- 'Shell',
535
- // There's a cache miss for B, because it hasn't been read yet. But not
536
- // A, because it was cached when we rendered the shell.
537
- 'Cache miss! [B]',
538
- 'Loading content...',
539
- ]);
540
- expect(root).toMatchRenderedOutput(
541
- <>
542
- <div>Shell</div>
543
- <div>Loading content...</div>
544
- </>,
545
- );
546
-
547
- await act(() => {
548
- resolveMostRecentTextCache('B');
549
- });
550
- assertLog(['Content']);
551
- expect(root).toMatchRenderedOutput(
552
- <>
553
- <div>Shell</div>
554
- <div>Content</div>
555
- </>,
556
- );
557
-
558
- await act(() => {
559
- root.render('Bye');
560
- });
561
- // no cleanup: cache is still retained at the root
562
- assertLog([]);
563
- expect(root).toMatchRenderedOutput('Bye');
564
- });
565
-
566
- // @gate enableCacheElement
567
- test('inner/ outer cache boundaries added in the same update use the same cache instance', async () => {
568
- const root = ReactNoop.createRoot();
569
-
570
- function App({showMore}) {
571
- return showMore ? (
572
- <Cache>
573
- <Suspense fallback={<Text text="Loading shell..." />}>
574
- {/* The shell reads A */}
575
- <Shell>
576
- {/* The inner content reads both A and B */}
577
- <Suspense fallback={<Text text="Loading content..." />}>
578
- <Cache>
579
- <Content />
580
- </Cache>
581
- </Suspense>
582
- </Shell>
583
- </Suspense>
584
- </Cache>
585
- ) : (
586
- '(empty)'
587
- );
588
- }
589
-
590
- function Shell({children}) {
591
- readText('A');
592
- return (
593
- <>
594
- <div>
595
- <Text text="Shell" />
596
- </div>
597
- <div>{children}</div>
598
- </>
599
- );
600
- }
601
-
602
- function Content() {
603
- readText('A');
604
- readText('B');
605
- return <Text text="Content" />;
606
- }
607
-
608
- await act(() => {
609
- root.render(<App showMore={false} />);
610
- });
611
- assertLog([]);
612
- expect(root).toMatchRenderedOutput('(empty)');
613
-
614
- await act(() => {
615
- root.render(<App showMore={true} />);
616
- });
617
- assertLog(['Cache miss! [A]', 'Loading shell...']);
618
- expect(root).toMatchRenderedOutput('Loading shell...');
619
-
620
- await act(() => {
621
- resolveMostRecentTextCache('A');
622
- });
623
- assertLog([
624
- 'Shell',
625
- // There's a cache miss for B, because it hasn't been read yet. But not
626
- // A, because it was cached when we rendered the shell.
627
- 'Cache miss! [B]',
628
- 'Loading content...',
629
- ]);
630
- expect(root).toMatchRenderedOutput(
631
- <>
632
- <div>Shell</div>
633
- <div>Loading content...</div>
634
- </>,
635
- );
636
-
637
- await act(() => {
638
- resolveMostRecentTextCache('B');
639
- });
640
- assertLog(['Content']);
641
- expect(root).toMatchRenderedOutput(
642
- <>
643
- <div>Shell</div>
644
- <div>Content</div>
645
- </>,
646
- );
647
-
648
- await act(() => {
649
- root.render('Bye');
650
- });
651
- assertLog(['Cache cleanup: A [v1]', 'Cache cleanup: B [v1]']);
652
- expect(root).toMatchRenderedOutput('Bye');
653
- });
654
-
655
- // @gate enableCacheElement
656
- test('refresh a cache boundary', async () => {
657
- let refresh;
658
- function App() {
659
- refresh = useCacheRefresh();
660
- return <AsyncText showVersion={true} text="A" />;
661
- }
662
-
663
- // Mount initial data
664
- const root = ReactNoop.createRoot();
665
- await act(() => {
666
- root.render(
667
- <Suspense fallback={<Text text="Loading..." />}>
668
- <App />
669
- </Suspense>,
670
- );
671
- });
672
- assertLog(['Cache miss! [A]', 'Loading...']);
673
- expect(root).toMatchRenderedOutput('Loading...');
674
-
675
- await act(() => {
676
- resolveMostRecentTextCache('A');
677
- });
678
- assertLog(['A [v1]']);
679
- expect(root).toMatchRenderedOutput('A [v1]');
680
-
681
- // Refresh for new data.
682
- await act(() => {
683
- startTransition(() => refresh());
684
- });
685
- assertLog(['Cache miss! [A]', 'Loading...']);
686
- expect(root).toMatchRenderedOutput('A [v1]');
687
-
688
- await act(() => {
689
- resolveMostRecentTextCache('A');
690
- });
691
- // Note that the version has updated
692
- if (getCacheSignal) {
693
- assertLog(['A [v2]', 'Cache cleanup: A [v1]']);
694
- } else {
695
- assertLog(['A [v2]']);
696
- }
697
- expect(root).toMatchRenderedOutput('A [v2]');
698
-
699
- await act(() => {
700
- root.render('Bye');
701
- });
702
- expect(root).toMatchRenderedOutput('Bye');
703
- });
704
-
705
- // @gate enableCacheElement
706
- test('refresh the root cache', async () => {
707
- let refresh;
708
- function App() {
709
- refresh = useCacheRefresh();
710
- return <AsyncText showVersion={true} text="A" />;
711
- }
712
-
713
- // Mount initial data
714
- const root = ReactNoop.createRoot();
715
- await act(() => {
716
- root.render(
717
- <Suspense fallback={<Text text="Loading..." />}>
718
- <App />
719
- </Suspense>,
720
- );
721
- });
722
- assertLog(['Cache miss! [A]', 'Loading...']);
723
- expect(root).toMatchRenderedOutput('Loading...');
724
-
725
- await act(() => {
726
- resolveMostRecentTextCache('A');
727
- });
728
- assertLog(['A [v1]']);
729
- expect(root).toMatchRenderedOutput('A [v1]');
730
-
731
- // Refresh for new data.
732
- await act(() => {
733
- startTransition(() => refresh());
734
- });
735
- assertLog(['Cache miss! [A]', 'Loading...']);
736
- expect(root).toMatchRenderedOutput('A [v1]');
737
-
738
- await act(() => {
739
- resolveMostRecentTextCache('A');
740
- });
741
- // Note that the version has updated, and the previous cache is cleared
742
- assertLog(['A [v2]', 'Cache cleanup: A [v1]']);
743
- expect(root).toMatchRenderedOutput('A [v2]');
744
-
745
- await act(() => {
746
- root.render('Bye');
747
- });
748
- // the original root cache already cleaned up when the refresh completed
749
- assertLog([]);
750
- expect(root).toMatchRenderedOutput('Bye');
751
- });
752
-
753
- // @gate enableCacheElement
754
- test('refresh the root cache without a transition', async () => {
755
- let refresh;
756
- function App() {
757
- refresh = useCacheRefresh();
758
- return <AsyncText showVersion={true} text="A" />;
759
- }
760
-
761
- // Mount initial data
762
- const root = ReactNoop.createRoot();
763
- await act(() => {
764
- root.render(
765
- <Suspense fallback={<Text text="Loading..." />}>
766
- <App />
767
- </Suspense>,
768
- );
769
- });
770
- assertLog(['Cache miss! [A]', 'Loading...']);
771
- expect(root).toMatchRenderedOutput('Loading...');
772
-
773
- await act(() => {
774
- resolveMostRecentTextCache('A');
775
- });
776
- assertLog(['A [v1]']);
777
- expect(root).toMatchRenderedOutput('A [v1]');
778
-
779
- // Refresh for new data.
780
- await act(() => {
781
- refresh();
782
- });
783
- assertLog([
784
- 'Cache miss! [A]',
785
- 'Loading...',
786
- // The v1 cache can be cleaned up since everything that references it has
787
- // been replaced by a fallback. When the boundary switches back to visible
788
- // it will use the v2 cache.
789
- 'Cache cleanup: A [v1]',
790
- ]);
791
- expect(root).toMatchRenderedOutput('Loading...');
792
-
793
- await act(() => {
794
- resolveMostRecentTextCache('A');
795
- });
796
- // Note that the version has updated, and the previous cache is cleared
797
- assertLog(['A [v2]']);
798
- expect(root).toMatchRenderedOutput('A [v2]');
799
-
800
- await act(() => {
801
- root.render('Bye');
802
- });
803
- // the original root cache already cleaned up when the refresh completed
804
- assertLog([]);
805
- expect(root).toMatchRenderedOutput('Bye');
806
- });
807
-
808
- // @gate enableCacheElement
809
- test('refresh a cache with seed data', async () => {
810
- let refresh;
811
- function App() {
812
- refresh = useCacheRefresh();
813
- return <AsyncText showVersion={true} text="A" />;
814
- }
815
-
816
- // Mount initial data
817
- const root = ReactNoop.createRoot();
818
- await act(() => {
819
- root.render(
820
- <Cache>
821
- <Suspense fallback={<Text text="Loading..." />}>
822
- <App />
823
- </Suspense>
824
- </Cache>,
825
- );
826
- });
827
- assertLog(['Cache miss! [A]', 'Loading...']);
828
- expect(root).toMatchRenderedOutput('Loading...');
829
-
830
- await act(() => {
831
- resolveMostRecentTextCache('A');
832
- });
833
- assertLog(['A [v1]']);
834
- expect(root).toMatchRenderedOutput('A [v1]');
835
-
836
- // Refresh for new data.
837
- await act(() => {
838
- // Refresh the cache with seeded data, like you would receive from a
839
- // server mutation.
840
- // TODO: Seeding multiple typed textCaches. Should work by calling `refresh`
841
- // multiple times with different key/value pairs
842
- startTransition(() => {
843
- const textCache = createTextCache();
844
- textCache.resolve('A');
845
- startTransition(() => refresh(createTextCache, textCache));
846
- });
847
- });
848
- // The root should re-render without a cache miss.
849
- // The cache is not cleared up yet, since it's still reference by the root
850
- assertLog(['A [v2]']);
851
- expect(root).toMatchRenderedOutput('A [v2]');
852
-
853
- await act(() => {
854
- root.render('Bye');
855
- });
856
- // the refreshed cache boundary is unmounted and cleans up
857
- assertLog(['Cache cleanup: A [v2]']);
858
- expect(root).toMatchRenderedOutput('Bye');
859
- });
860
-
861
- // @gate enableCacheElement
862
- test('refreshing a parent cache also refreshes its children', async () => {
863
- let refreshShell;
864
- function RefreshShell() {
865
- refreshShell = useCacheRefresh();
866
- return null;
867
- }
868
-
869
- function App({showMore}) {
870
- return (
871
- <Cache>
872
- <RefreshShell />
873
- <Suspense fallback={<Text text="Loading..." />}>
874
- <AsyncText showVersion={true} text="A" />
875
- </Suspense>
876
- {showMore ? (
877
- <Cache>
878
- <Suspense fallback={<Text text="Loading..." />}>
879
- <AsyncText showVersion={true} text="A" />
880
- </Suspense>
881
- </Cache>
882
- ) : null}
883
- </Cache>
884
- );
885
- }
886
-
887
- const root = ReactNoop.createRoot();
888
- await act(() => {
889
- seedNextTextCache('A');
890
- root.render(<App showMore={false} />);
891
- });
892
- assertLog(['A [v1]']);
893
- expect(root).toMatchRenderedOutput('A [v1]');
894
-
895
- // Add a new cache boundary
896
- await act(() => {
897
- seedNextTextCache('A');
898
- root.render(<App showMore={true} />);
899
- });
900
- assertLog([
901
- 'A [v1]',
902
- // New tree should load fresh data.
903
- 'A [v2]',
904
- ]);
905
- expect(root).toMatchRenderedOutput('A [v1]A [v2]');
906
-
907
- // Now refresh the shell. This should also cause the "Show More" contents to
908
- // refresh, since its cache is nested inside the outer one.
909
- await act(() => {
910
- startTransition(() => refreshShell());
911
- });
912
- assertLog(['Cache miss! [A]', 'Loading...', 'Loading...']);
913
- expect(root).toMatchRenderedOutput('A [v1]A [v2]');
914
-
915
- await act(() => {
916
- resolveMostRecentTextCache('A');
917
- });
918
- assertLog([
919
- 'A [v3]',
920
- 'A [v3]',
921
- // once the refresh completes the inner showMore boundary frees its previous
922
- // cache instance, since it is now using the refreshed parent instance.
923
- 'Cache cleanup: A [v2]',
924
- ]);
925
- expect(root).toMatchRenderedOutput('A [v3]A [v3]');
926
-
927
- await act(() => {
928
- root.render('Bye!');
929
- });
930
- // Unmounting children releases the refreshed cache instance only; the root
931
- // still retains the original cache instance used for the first render
932
- assertLog(['Cache cleanup: A [v3]']);
933
- expect(root).toMatchRenderedOutput('Bye!');
934
- });
935
-
936
- // @gate enableCacheElement
937
- test(
938
- 'refreshing a cache boundary does not refresh the other boundaries ' +
939
- 'that mounted at the same time (i.e. the ones that share the same cache)',
940
- async () => {
941
- let refreshFirstBoundary;
942
- function RefreshFirstBoundary() {
943
- refreshFirstBoundary = useCacheRefresh();
944
- return null;
945
- }
946
-
947
- function App({showMore}) {
948
- return showMore ? (
949
- <>
950
- <Cache>
951
- <Suspense fallback={<Text text="Loading..." />}>
952
- <RefreshFirstBoundary />
953
- <AsyncText showVersion={true} text="A" />
954
- </Suspense>
955
- </Cache>
956
- <Cache>
957
- <Suspense fallback={<Text text="Loading..." />}>
958
- <AsyncText showVersion={true} text="A" />
959
- </Suspense>
960
- </Cache>
961
- </>
962
- ) : null;
963
- }
964
-
965
- // First mount the initial shell without the nested boundaries. This is
966
- // necessary for this test because we want the two inner boundaries to be
967
- // treated like sibling providers that happen to share an underlying
968
- // cache, as opposed to consumers of the root-level cache.
969
- const root = ReactNoop.createRoot();
970
- await act(() => {
971
- root.render(<App showMore={false} />);
972
- });
973
-
974
- // Now reveal the boundaries. In a real app this would be a navigation.
975
- await act(() => {
976
- root.render(<App showMore={true} />);
977
- });
978
-
979
- // Even though there are two new <Cache /> trees, they should share the same
980
- // data cache. So there should be only a single cache miss for A.
981
- assertLog(['Cache miss! [A]', 'Loading...', 'Loading...']);
982
- expect(root).toMatchRenderedOutput('Loading...Loading...');
983
-
984
- await act(() => {
985
- resolveMostRecentTextCache('A');
986
- });
987
- assertLog(['A [v1]', 'A [v1]']);
988
- expect(root).toMatchRenderedOutput('A [v1]A [v1]');
989
-
990
- // Refresh the first boundary. It should not refresh the second boundary,
991
- // even though they previously shared the same underlying cache.
992
- await act(async () => {
993
- await refreshFirstBoundary();
994
- });
995
- assertLog(['Cache miss! [A]', 'Loading...']);
996
-
997
- await act(() => {
998
- resolveMostRecentTextCache('A');
999
- });
1000
- assertLog(['A [v2]']);
1001
- expect(root).toMatchRenderedOutput('A [v2]A [v1]');
1002
-
1003
- // Unmount children: this should clear *both* cache instances:
1004
- // the root doesn't have a cache instance (since it wasn't accessed
1005
- // during the initial render, and all subsequent cache accesses were within
1006
- // a fresh boundary). Therefore this causes cleanup for both the fresh cache
1007
- // instance in the refreshed first boundary and cleanup for the non-refreshed
1008
- // sibling boundary.
1009
- await act(() => {
1010
- root.render('Bye!');
1011
- });
1012
- assertLog(['Cache cleanup: A [v2]', 'Cache cleanup: A [v1]']);
1013
- expect(root).toMatchRenderedOutput('Bye!');
1014
- },
1015
- );
1016
-
1017
- // @gate enableCacheElement
1018
- test(
1019
- 'mount a new Cache boundary in a sibling while simultaneously ' +
1020
- 'resolving a Suspense boundary',
1021
- async () => {
1022
- function App({showMore}) {
1023
- return (
1024
- <>
1025
- {showMore ? (
1026
- <Suspense fallback={<Text text="Loading..." />}>
1027
- <Cache>
1028
- <AsyncText showVersion={true} text="A" />
1029
- </Cache>
1030
- </Suspense>
1031
- ) : null}
1032
- <Suspense fallback={<Text text="Loading..." />}>
1033
- <Cache>
1034
- {' '}
1035
- <AsyncText showVersion={true} text="A" />{' '}
1036
- <AsyncText showVersion={true} text="B" />
1037
- </Cache>
1038
- </Suspense>
1039
- </>
1040
- );
1041
- }
1042
-
1043
- const root = ReactNoop.createRoot();
1044
- await act(() => {
1045
- root.render(<App showMore={false} />);
1046
- });
1047
- assertLog(['Cache miss! [A]', 'Loading...']);
1048
- expect(root).toMatchRenderedOutput('Loading...');
1049
-
1050
- await act(() => {
1051
- // This will resolve the content in the first cache
1052
- resolveMostRecentTextCache('A');
1053
- resolveMostRecentTextCache('B');
1054
- // And mount the second tree, which includes new content
1055
- root.render(<App showMore={true} />);
1056
- });
1057
- assertLog([
1058
- // The new tree should use a fresh cache
1059
- 'Cache miss! [A]',
1060
- 'Loading...',
1061
- // The other tree uses the cached responses. This demonstrates that the
1062
- // requests are not dropped.
1063
- 'A [v1]',
1064
- 'B [v1]',
1065
- ]);
1066
- expect(root).toMatchRenderedOutput('Loading... A [v1] B [v1]');
1067
-
1068
- // Now resolve the second tree
1069
- await act(() => {
1070
- resolveMostRecentTextCache('A');
1071
- });
1072
- assertLog(['A [v2]']);
1073
- expect(root).toMatchRenderedOutput('A [v2] A [v1] B [v1]');
1074
-
1075
- await act(() => {
1076
- root.render('Bye!');
1077
- });
1078
- // Unmounting children releases both cache boundaries, but the original
1079
- // cache instance (used by second boundary) is still referenced by the root.
1080
- // only the second cache instance is freed.
1081
- assertLog(['Cache cleanup: A [v2]']);
1082
- expect(root).toMatchRenderedOutput('Bye!');
1083
- },
1084
- );
1085
-
1086
- // @gate enableCacheElement
1087
- test('cache pool is cleared once transitions that depend on it commit their shell', async () => {
1088
- function Child({text}) {
1089
- return (
1090
- <Cache>
1091
- <AsyncText showVersion={true} text={text} />
1092
- </Cache>
1093
- );
1094
- }
1095
-
1096
- const root = ReactNoop.createRoot();
1097
- await act(() => {
1098
- root.render(
1099
- <Suspense fallback={<Text text="Loading..." />}>(empty)</Suspense>,
1100
- );
1101
- });
1102
- assertLog([]);
1103
- expect(root).toMatchRenderedOutput('(empty)');
1104
-
1105
- await act(() => {
1106
- startTransition(() => {
1107
- root.render(
1108
- <Suspense fallback={<Text text="Loading..." />}>
1109
- <Child text="A" />
1110
- </Suspense>,
1111
- );
1112
- });
1113
- });
1114
- assertLog(['Cache miss! [A]', 'Loading...']);
1115
- expect(root).toMatchRenderedOutput('(empty)');
1116
-
1117
- await act(() => {
1118
- startTransition(() => {
1119
- root.render(
1120
- <Suspense fallback={<Text text="Loading..." />}>
1121
- <Child text="A" />
1122
- <Child text="A" />
1123
- </Suspense>,
1124
- );
1125
- });
1126
- });
1127
- assertLog([
1128
- // No cache miss, because it uses the pooled cache
1129
- 'Loading...',
1130
- ]);
1131
- expect(root).toMatchRenderedOutput('(empty)');
1132
-
1133
- // Resolve the request
1134
- await act(() => {
1135
- resolveMostRecentTextCache('A');
1136
- });
1137
- assertLog(['A [v1]', 'A [v1]']);
1138
- expect(root).toMatchRenderedOutput('A [v1]A [v1]');
1139
-
1140
- // Now do another transition
1141
- await act(() => {
1142
- startTransition(() => {
1143
- root.render(
1144
- <Suspense fallback={<Text text="Loading..." />}>
1145
- <Child text="A" />
1146
- <Child text="A" />
1147
- <Child text="A" />
1148
- </Suspense>,
1149
- );
1150
- });
1151
- });
1152
- assertLog([
1153
- // First two children use the old cache because they already finished
1154
- 'A [v1]',
1155
- 'A [v1]',
1156
- // The new child uses a fresh cache
1157
- 'Cache miss! [A]',
1158
- 'Loading...',
1159
- ]);
1160
- expect(root).toMatchRenderedOutput('A [v1]A [v1]');
1161
-
1162
- await act(() => {
1163
- resolveMostRecentTextCache('A');
1164
- });
1165
- assertLog(['A [v1]', 'A [v1]', 'A [v2]']);
1166
- expect(root).toMatchRenderedOutput('A [v1]A [v1]A [v2]');
1167
-
1168
- // Unmount children: the first text cache instance is created only after the root
1169
- // commits, so both fresh cache instances are released by their cache boundaries,
1170
- // cleaning up v1 (used for the first two children which render together) and
1171
- // v2 (used for the third boundary added later).
1172
- await act(() => {
1173
- root.render('Bye!');
1174
- });
1175
- assertLog(['Cache cleanup: A [v1]', 'Cache cleanup: A [v2]']);
1176
- expect(root).toMatchRenderedOutput('Bye!');
1177
- });
1178
-
1179
- // @gate enableCacheElement
1180
- test('cache pool is not cleared by arbitrary commits', async () => {
1181
- function App() {
1182
- return (
1183
- <>
1184
- <ShowMore />
1185
- <Unrelated />
1186
- </>
1187
- );
1188
- }
1189
-
1190
- let showMore;
1191
- function ShowMore() {
1192
- const [shouldShow, _showMore] = useState(false);
1193
- showMore = () => _showMore(true);
1194
- return (
1195
- <>
1196
- <Suspense fallback={<Text text="Loading..." />}>
1197
- {shouldShow ? (
1198
- <Cache>
1199
- <AsyncText showVersion={true} text="A" />
1200
- </Cache>
1201
- ) : null}
1202
- </Suspense>
1203
- </>
1204
- );
1205
- }
1206
-
1207
- let updateUnrelated;
1208
- function Unrelated() {
1209
- const [count, _updateUnrelated] = useState(0);
1210
- updateUnrelated = _updateUnrelated;
1211
- return <Text text={String(count)} />;
1212
- }
1213
-
1214
- const root = ReactNoop.createRoot();
1215
- await act(() => {
1216
- root.render(<App />);
1217
- });
1218
- assertLog(['0']);
1219
- expect(root).toMatchRenderedOutput('0');
1220
-
1221
- await act(() => {
1222
- startTransition(() => {
1223
- showMore();
1224
- });
1225
- });
1226
- assertLog(['Cache miss! [A]', 'Loading...']);
1227
- expect(root).toMatchRenderedOutput('0');
1228
-
1229
- await act(() => {
1230
- updateUnrelated(1);
1231
- });
1232
- assertLog([
1233
- '1',
1234
-
1235
- // Happens to re-render the fallback. Doesn't need to, but not relevant
1236
- // to this test.
1237
- 'Loading...',
1238
- ]);
1239
- expect(root).toMatchRenderedOutput('1');
1240
-
1241
- await act(() => {
1242
- resolveMostRecentTextCache('A');
1243
- });
1244
- assertLog(['A [v1]']);
1245
- expect(root).toMatchRenderedOutput('A [v1]1');
1246
-
1247
- // Unmount children: the first text cache instance is created only after initial
1248
- // render after calling showMore(). This instance is cleaned up when that boundary
1249
- // is unmounted. Bc root cache instance is never accessed, the inner cache
1250
- // boundary ends up at v1.
1251
- await act(() => {
1252
- root.render('Bye!');
1253
- });
1254
- assertLog(['Cache cleanup: A [v1]']);
1255
- expect(root).toMatchRenderedOutput('Bye!');
1256
- });
1257
-
1258
- // @gate enableCacheElement
1259
- test('cache boundary uses a fresh cache when its key changes', async () => {
1260
- const root = ReactNoop.createRoot();
1261
- seedNextTextCache('A');
1262
- await act(() => {
1263
- root.render(
1264
- <Suspense fallback="Loading...">
1265
- <Cache key="A">
1266
- <AsyncText showVersion={true} text="A" />
1267
- </Cache>
1268
- </Suspense>,
1269
- );
1270
- });
1271
- assertLog(['A [v1]']);
1272
- expect(root).toMatchRenderedOutput('A [v1]');
1273
-
1274
- seedNextTextCache('B');
1275
- await act(() => {
1276
- root.render(
1277
- <Suspense fallback="Loading...">
1278
- <Cache key="B">
1279
- <AsyncText showVersion={true} text="B" />
1280
- </Cache>
1281
- </Suspense>,
1282
- );
1283
- });
1284
- assertLog(['B [v2]']);
1285
- expect(root).toMatchRenderedOutput('B [v2]');
1286
-
1287
- // Unmount children: the fresh cache instance for B cleans up since the cache boundary
1288
- // is the only owner, while the original cache instance (for A) is still retained by
1289
- // the root.
1290
- await act(() => {
1291
- root.render('Bye!');
1292
- });
1293
- assertLog(['Cache cleanup: B [v2]']);
1294
- expect(root).toMatchRenderedOutput('Bye!');
1295
- });
1296
-
1297
- // @gate enableCacheElement
1298
- test('overlapping transitions after an initial mount use the same fresh cache', async () => {
1299
- const root = ReactNoop.createRoot();
1300
- await act(() => {
1301
- root.render(
1302
- <Suspense fallback="Loading...">
1303
- <Cache key="A">
1304
- <AsyncText showVersion={true} text="A" />
1305
- </Cache>
1306
- </Suspense>,
1307
- );
1308
- });
1309
- assertLog(['Cache miss! [A]']);
1310
- expect(root).toMatchRenderedOutput('Loading...');
1311
-
1312
- await act(() => {
1313
- resolveMostRecentTextCache('A');
1314
- });
1315
- assertLog(['A [v1]']);
1316
- expect(root).toMatchRenderedOutput('A [v1]');
1317
-
1318
- // After a mount, subsequent transitions use a fresh cache
1319
- await act(() => {
1320
- startTransition(() => {
1321
- root.render(
1322
- <Suspense fallback="Loading...">
1323
- <Cache key="B">
1324
- <AsyncText showVersion={true} text="B" />
1325
- </Cache>
1326
- </Suspense>,
1327
- );
1328
- });
1329
- });
1330
- assertLog(['Cache miss! [B]']);
1331
- expect(root).toMatchRenderedOutput('A [v1]');
1332
-
1333
- // Update to a different text and with a different key for the cache
1334
- // boundary: this should still use the fresh cache instance created
1335
- // for the earlier transition
1336
- await act(() => {
1337
- startTransition(() => {
1338
- root.render(
1339
- <Suspense fallback="Loading...">
1340
- <Cache key="C">
1341
- <AsyncText showVersion={true} text="C" />
1342
- </Cache>
1343
- </Suspense>,
1344
- );
1345
- });
1346
- });
1347
- assertLog(['Cache miss! [C]']);
1348
- expect(root).toMatchRenderedOutput('A [v1]');
1349
-
1350
- await act(() => {
1351
- resolveMostRecentTextCache('C');
1352
- });
1353
- assertLog(['C [v2]']);
1354
- expect(root).toMatchRenderedOutput('C [v2]');
1355
-
1356
- // Unmount children: the fresh cache used for the updates is freed, while the
1357
- // original cache (with A) is still retained at the root.
1358
- await act(() => {
1359
- root.render('Bye!');
1360
- });
1361
- assertLog(['Cache cleanup: B [v2]', 'Cache cleanup: C [v2]']);
1362
- expect(root).toMatchRenderedOutput('Bye!');
1363
- });
1364
-
1365
- // @gate enableCacheElement
1366
- test('overlapping updates after an initial mount use the same fresh cache', async () => {
1367
- const root = ReactNoop.createRoot();
1368
- await act(() => {
1369
- root.render(
1370
- <Suspense fallback="Loading...">
1371
- <Cache key="A">
1372
- <AsyncText showVersion={true} text="A" />
1373
- </Cache>
1374
- </Suspense>,
1375
- );
1376
- });
1377
- assertLog(['Cache miss! [A]']);
1378
- expect(root).toMatchRenderedOutput('Loading...');
1379
-
1380
- await act(() => {
1381
- resolveMostRecentTextCache('A');
1382
- });
1383
- assertLog(['A [v1]']);
1384
- expect(root).toMatchRenderedOutput('A [v1]');
1385
-
1386
- // After a mount, subsequent updates use a fresh cache
1387
- await act(() => {
1388
- root.render(
1389
- <Suspense fallback="Loading...">
1390
- <Cache key="B">
1391
- <AsyncText showVersion={true} text="B" />
1392
- </Cache>
1393
- </Suspense>,
1394
- );
1395
- });
1396
- assertLog(['Cache miss! [B]']);
1397
- expect(root).toMatchRenderedOutput('Loading...');
1398
-
1399
- // A second update uses the same fresh cache: even though this is a new
1400
- // Cache boundary, the render uses the fresh cache from the pending update.
1401
- await act(() => {
1402
- root.render(
1403
- <Suspense fallback="Loading...">
1404
- <Cache key="C">
1405
- <AsyncText showVersion={true} text="C" />
1406
- </Cache>
1407
- </Suspense>,
1408
- );
1409
- });
1410
- assertLog(['Cache miss! [C]']);
1411
- expect(root).toMatchRenderedOutput('Loading...');
1412
-
1413
- await act(() => {
1414
- resolveMostRecentTextCache('C');
1415
- });
1416
- assertLog(['C [v2]']);
1417
- expect(root).toMatchRenderedOutput('C [v2]');
1418
-
1419
- // Unmount children: the fresh cache used for the updates is freed, while the
1420
- // original cache (with A) is still retained at the root.
1421
- await act(() => {
1422
- root.render('Bye!');
1423
- });
1424
- assertLog(['Cache cleanup: B [v2]', 'Cache cleanup: C [v2]']);
1425
- expect(root).toMatchRenderedOutput('Bye!');
1426
- });
1427
-
1428
- // @gate enableCacheElement
1429
- test('cleans up cache only used in an aborted transition', async () => {
1430
- const root = ReactNoop.createRoot();
1431
- seedNextTextCache('A');
1432
- await act(() => {
1433
- root.render(
1434
- <Suspense fallback="Loading...">
1435
- <Cache key="A">
1436
- <AsyncText showVersion={true} text="A" />
1437
- </Cache>
1438
- </Suspense>,
1439
- );
1440
- });
1441
- assertLog(['A [v1]']);
1442
- expect(root).toMatchRenderedOutput('A [v1]');
1443
-
1444
- // Start a transition from A -> B..., which should create a fresh cache
1445
- // for the new cache boundary (bc of the different key)
1446
- await act(() => {
1447
- startTransition(() => {
1448
- root.render(
1449
- <Suspense fallback="Loading...">
1450
- <Cache key="B">
1451
- <AsyncText showVersion={true} text="B" />
1452
- </Cache>
1453
- </Suspense>,
1454
- );
1455
- });
1456
- });
1457
- assertLog(['Cache miss! [B]']);
1458
- expect(root).toMatchRenderedOutput('A [v1]');
1459
-
1460
- // ...but cancel by transitioning "back" to A (which we never really left)
1461
- await act(() => {
1462
- startTransition(() => {
1463
- root.render(
1464
- <Suspense fallback="Loading...">
1465
- <Cache key="A">
1466
- <AsyncText showVersion={true} text="A" />
1467
- </Cache>
1468
- </Suspense>,
1469
- );
1470
- });
1471
- });
1472
- assertLog(['A [v1]', 'Cache cleanup: B [v2]']);
1473
- expect(root).toMatchRenderedOutput('A [v1]');
1474
-
1475
- // Unmount children: ...
1476
- await act(() => {
1477
- root.render('Bye!');
1478
- });
1479
- assertLog([]);
1480
- expect(root).toMatchRenderedOutput('Bye!');
1481
- });
1482
-
1483
- // @gate enableCacheElement
1484
- test.skip('if a root cache refresh never commits its fresh cache is released', async () => {
1485
- const root = ReactNoop.createRoot();
1486
- let refresh;
1487
- function Example({text}) {
1488
- refresh = useCacheRefresh();
1489
- return <AsyncText showVersion={true} text={text} />;
1490
- }
1491
- seedNextTextCache('A');
1492
- await act(() => {
1493
- root.render(
1494
- <Suspense fallback="Loading...">
1495
- <Example text="A" />
1496
- </Suspense>,
1497
- );
1498
- });
1499
- assertLog(['A [v1]']);
1500
- expect(root).toMatchRenderedOutput('A [v1]');
1501
-
1502
- await act(() => {
1503
- startTransition(() => {
1504
- refresh();
1505
- });
1506
- });
1507
- assertLog(['Cache miss! [A]']);
1508
- expect(root).toMatchRenderedOutput('A [v1]');
1509
-
1510
- await act(() => {
1511
- root.render('Bye!');
1512
- });
1513
- assertLog([
1514
- // TODO: the v1 cache should *not* be cleaned up, it is still retained by the root
1515
- // The following line is presently yielded but should not be:
1516
- // 'Cache cleanup: A [v1]',
1517
-
1518
- // TODO: the v2 cache *should* be cleaned up, it was created for the abandoned refresh
1519
- // The following line is presently not yielded but should be:
1520
- 'Cache cleanup: A [v2]',
1521
- ]);
1522
- expect(root).toMatchRenderedOutput('Bye!');
1523
- });
1524
-
1525
- // @gate enableCacheElement
1526
- test.skip('if a cache boundary refresh never commits its fresh cache is released', async () => {
1527
- const root = ReactNoop.createRoot();
1528
- let refresh;
1529
- function Example({text}) {
1530
- refresh = useCacheRefresh();
1531
- return <AsyncText showVersion={true} text={text} />;
1532
- }
1533
- seedNextTextCache('A');
1534
- await act(() => {
1535
- root.render(
1536
- <Suspense fallback="Loading...">
1537
- <Cache>
1538
- <Example text="A" />
1539
- </Cache>
1540
- </Suspense>,
1541
- );
1542
- });
1543
- assertLog(['A [v1]']);
1544
- expect(root).toMatchRenderedOutput('A [v1]');
1545
-
1546
- await act(() => {
1547
- startTransition(() => {
1548
- refresh();
1549
- });
1550
- });
1551
- assertLog(['Cache miss! [A]']);
1552
- expect(root).toMatchRenderedOutput('A [v1]');
1553
-
1554
- // Unmount the boundary before the refresh can complete
1555
- await act(() => {
1556
- root.render('Bye!');
1557
- });
1558
- assertLog([
1559
- // TODO: the v2 cache *should* be cleaned up, it was created for the abandoned refresh
1560
- // The following line is presently not yielded but should be:
1561
- 'Cache cleanup: A [v2]',
1562
- ]);
1563
- expect(root).toMatchRenderedOutput('Bye!');
1564
- });
1565
-
1566
- // @gate enableActivity
1567
- // @gate enableCache
1568
- test('prerender a new cache boundary inside an Activity tree', async () => {
1569
- function App({prerenderMore}) {
1570
- return (
1571
- <Activity mode="hidden">
1572
- <div>
1573
- {prerenderMore ? (
1574
- <Cache>
1575
- <AsyncText text="More" />
1576
- </Cache>
1577
- ) : null}
1578
- </div>
1579
- </Activity>
1580
- );
1581
- }
1582
-
1583
- const root = ReactNoop.createRoot();
1584
- await act(() => {
1585
- root.render(<App prerenderMore={false} />);
1586
- });
1587
- assertLog([]);
1588
- expect(root).toMatchRenderedOutput(<div hidden={true} />);
1589
-
1590
- seedNextTextCache('More');
1591
- await act(() => {
1592
- root.render(<App prerenderMore={true} />);
1593
- });
1594
- assertLog(['More']);
1595
- expect(root).toMatchRenderedOutput(<div hidden={true}>More</div>);
1596
- });
1597
-});
packages/react/index.classic.fb.js
-1
@@ -29,7 +29,6 @@ export {
29
cache,
30
startTransition,
31
unstable_Activity,
32
- unstable_Cache,
32
unstable_TracingMarker,
33
unstable_DebugTracingMode,
34
unstable_LegacyHidden,
packages/react/index.experimental.js
-1
@@ -28,7 +28,6 @@ export {
28
memo,
29
cache,
30
startTransition,
31
- unstable_Cache,
31
unstable_DebugTracingMode,
32
unstable_Activity,
33
unstable_postpone,
packages/react/index.js
-1
@@ -49,7 +49,6 @@ export {
49
memo,
50
cache,
51
startTransition,
52
- unstable_Cache,
52
unstable_DebugTracingMode,
53
unstable_LegacyHidden,
54
unstable_Activity,
packages/react/index.modern.fb.js
-1
@@ -28,7 +28,6 @@ export {
28
memo,
29
cache,
30
startTransition,
31
- unstable_Cache,
31
unstable_DebugTracingMode,
32
unstable_LegacyHidden,
33
unstable_Activity,
packages/react/src/ReactClient.js
-2
@@ -18,7 +18,6 @@ import {
18
REACT_LEGACY_HIDDEN_TYPE,
19
REACT_OFFSCREEN_TYPE,
20
REACT_SCOPE_TYPE,
21
- REACT_CACHE_TYPE,
21
REACT_TRACING_MARKER_TYPE,
22
} from 'shared/ReactSymbols';
23
@@ -119,7 +118,6 @@ export {
118
getCacheSignal as unstable_getCacheSignal,
119
getCacheForType as unstable_getCacheForType,
120
useCacheRefresh as unstable_useCacheRefresh,
122
- REACT_CACHE_TYPE as unstable_Cache,
121
use,
122
useMemoCache as unstable_useMemoCache,
123
// enableScopeAPI
packages/shared/ReactFeatureFlags.js
-1
@@ -75,7 +75,6 @@ export const enableLegacyFBSupport = false;
75
76
export const enableCache = true;
77
export const enableLegacyCache = __EXPERIMENTAL__;
78
-export const enableCacheElement = __EXPERIMENTAL__;
78
export const enableFetchInstrumentation = true;
79
80
export const enableBinaryFlight = __EXPERIMENTAL__;
packages/shared/ReactSymbols.js
-1
@@ -35,7 +35,6 @@ export const REACT_OFFSCREEN_TYPE: symbol = Symbol.for('react.offscreen');
35
export const REACT_LEGACY_HIDDEN_TYPE: symbol = Symbol.for(
36
'react.legacy_hidden',
37
);
38
-export const REACT_CACHE_TYPE: symbol = Symbol.for('react.cache');
38
export const REACT_TRACING_MARKER_TYPE: symbol = Symbol.for(
39
'react.tracing_marker',
40
);
packages/shared/forks/ReactFeatureFlags.native-fb.js
-1
@@ -43,7 +43,6 @@ export const enableProfilerNestedUpdatePhase = __PROFILE__;
43
export const enableUpdaterTracking = __PROFILE__;
44
export const enableCache = true;
45
export const enableLegacyCache = false;
46
-export const enableCacheElement = true;
46
export const enableFetchInstrumentation = false;
47
export const enableBinaryFlight = true;
48
export const enableTaint = true;
packages/shared/forks/ReactFeatureFlags.native-oss.js
-1
@@ -48,7 +48,6 @@ export const enableRenderableContext = __TODO_NEXT_RN_MAJOR__;
48
const __NEXT_RN_MAJOR__ = true;
49
export const disableClientCache = __NEXT_RN_MAJOR__;
50
export const disableLegacyContext = __NEXT_RN_MAJOR__;
51
-export const enableCacheElement = __NEXT_RN_MAJOR__;
51
export const enableTaint = __NEXT_RN_MAJOR__;
52
export const enableUnifiedSyncLane = __NEXT_RN_MAJOR__;
53
export const enableFizzExternalRuntime = __NEXT_RN_MAJOR__; // DOM-only
packages/shared/forks/ReactFeatureFlags.test-renderer.js
-1
@@ -20,7 +20,6 @@ export const enableProfilerNestedUpdatePhase = __PROFILE__;
20
export const enableUpdaterTracking = false;
21
export const enableCache = true;
22
export const enableLegacyCache = __EXPERIMENTAL__;
23
-export const enableCacheElement = __EXPERIMENTAL__;
23
export const enableFetchInstrumentation = true;
24
export const enableBinaryFlight = true;
25
export const enableTaint = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js
-1
@@ -20,7 +20,6 @@ export const enableProfilerNestedUpdatePhase = __PROFILE__;
20
export const enableUpdaterTracking = false;
21
export const enableCache = true;
22
export const enableLegacyCache = false;
23
-export const enableCacheElement = true;
23
export const enableFetchInstrumentation = false;
24
export const enableBinaryFlight = true;
25
export const enableTaint = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
-1
@@ -20,7 +20,6 @@ export const enableProfilerNestedUpdatePhase = __PROFILE__;
20
export const enableUpdaterTracking = false;
21
export const enableCache = true;
22
export const enableLegacyCache = true;
23
-export const enableCacheElement = true;
23
export const enableFetchInstrumentation = false;
24
export const enableBinaryFlight = true;
25
export const enableTaint = true;
packages/shared/forks/ReactFeatureFlags.www.js
-1
@@ -67,7 +67,6 @@ export const enableGetInspectorDataForInstanceInProduction = false;
67
68
export const enableCache = true;
69
export const enableLegacyCache = true;
70
-export const enableCacheElement = true;
70
export const enableFetchInstrumentation = false;
71
72
export const enableBinaryFlight = false;
packages/shared/getComponentNameFromType.js
-6
@@ -23,13 +23,11 @@ import {
23
REACT_SUSPENSE_TYPE,
24
REACT_SUSPENSE_LIST_TYPE,
25
REACT_LAZY_TYPE,
26
- REACT_CACHE_TYPE,
26
REACT_TRACING_MARKER_TYPE,
27
} from 'shared/ReactSymbols';
28
29
import {
30
enableTransitionTracing,
32
- enableCache,
31
enableRenderableContext,
32
} from './ReactFeatureFlags';
33
@@ -83,10 +81,6 @@ export default function getComponentNameFromType(type: mixed): string | null {
81
return 'Suspense';
82
case REACT_SUSPENSE_LIST_TYPE:
83
return 'SuspenseList';
86
- case REACT_CACHE_TYPE:
87
- if (enableCache) {
88
- return 'Cache';
89
- }
84
// Fall through
85
case REACT_TRACING_MARKER_TYPE:
86
if (enableTransitionTracing) {
packages/shared/isValidElementType.js
-3
@@ -23,12 +23,10 @@ import {
23
REACT_SCOPE_TYPE,
24
REACT_LEGACY_HIDDEN_TYPE,
25
REACT_OFFSCREEN_TYPE,
26
- REACT_CACHE_TYPE,
26
REACT_TRACING_MARKER_TYPE,
27
} from 'shared/ReactSymbols';
28
import {
29
enableScopeAPI,
31
- enableCacheElement,
30
enableTransitionTracing,
31
enableDebugTracing,
32
enableLegacyHidden,
@@ -53,7 +51,6 @@ export default function isValidElementType(type: mixed): boolean {
51
(enableLegacyHidden && type === REACT_LEGACY_HIDDEN_TYPE) ||
52
type === REACT_OFFSCREEN_TYPE ||
53
(enableScopeAPI && type === REACT_SCOPE_TYPE) ||
56
- (enableCacheElement && type === REACT_CACHE_TYPE) ||
54
(enableTransitionTracing && type === REACT_TRACING_MARKER_TYPE)
55
) {
56
return true;