@@ -2,16 +2,17 @@ let React;
2
2
let ReactNoop ;
3
3
let Cache ;
4
4
let getCacheSignal ;
5
- let getCacheForType ;
6
5
let Scheduler ;
7
6
let act ;
8
7
let Suspense ;
9
8
let Offscreen ;
10
9
let useCacheRefresh ;
11
10
let startTransition ;
12
11
let useState ;
12
+ let cache ;
13
13
14
- let caches ;
14
+ let getTextCache ;
15
+ let textCaches ;
15
16
let seededCache ;
16
17
17
18
describe ( 'ReactCache' , ( ) => {
@@ -24,66 +25,68 @@ describe('ReactCache', () => {
24
25
Scheduler = require ( 'scheduler' ) ;
25
26
act = require ( 'jest-react' ) . act ;
26
27
Suspense = React . Suspense ;
28
+ cache = React . experimental_cache ;
27
29
Offscreen = React . unstable_Offscreen ;
28
30
getCacheSignal = React . unstable_getCacheSignal ;
29
- getCacheForType = React . unstable_getCacheForType ;
30
31
useCacheRefresh = React . unstable_useCacheRefresh ;
31
32
startTransition = React . startTransition ;
32
33
useState = React . useState ;
33
34
34
- caches = [ ] ;
35
+ textCaches = [ ] ;
35
36
seededCache = null ;
36
- } ) ;
37
-
38
- function createTextCache ( ) {
39
- if ( seededCache !== null ) {
40
- // Trick to seed a cache before it exists.
41
- // TODO: Need a built-in API to seed data before the initial render (i.e.
42
- // not a refresh because nothing has mounted yet).
43
- const cache = seededCache ;
44
- seededCache = null ;
45
- return cache ;
46
- }
47
37
48
- const data = new Map ( ) ;
49
- const version = caches . length + 1 ;
50
- const cache = {
51
- version,
52
- data,
53
- resolve ( text ) {
54
- const record = data . get ( text ) ;
55
- if ( record === undefined ) {
56
- const newRecord = {
57
- status : 'resolved' ,
58
- value : text ,
59
- cleanupScheduled : false ,
60
- } ;
61
- data . set ( text , newRecord ) ;
62
- } else if ( record . status === 'pending' ) {
63
- record . value . resolve ( ) ;
38
+ if ( gate ( flags => flags . enableCache ) ) {
39
+ getTextCache = cache ( ( ) => {
40
+ if ( seededCache !== null ) {
41
+ // Trick to seed a cache before it exists.
42
+ // TODO: Need a built-in API to seed data before the initial render (i.e.
43
+ // not a refresh because nothing has mounted yet).
44
+ const textCache = seededCache ;
45
+ seededCache = null ;
46
+ return textCache ;
64
47
}
65
- } ,
66
- reject ( text , error ) {
67
- const record = data . get ( text ) ;
68
- if ( record === undefined ) {
69
- const newRecord = {
70
- status : 'rejected' ,
71
- value : error ,
72
- cleanupScheduled : false ,
73
- } ;
74
- data . set ( text , newRecord ) ;
75
- } else if ( record . status === 'pending' ) {
76
- record . value . reject ( ) ;
77
- }
78
- } ,
79
- } ;
80
- caches . push ( cache ) ;
81
- return cache ;
82
- }
48
+
49
+ const data = new Map ( ) ;
50
+ const version = textCaches . length + 1 ;
51
+ const textCache = {
52
+ version,
53
+ data,
54
+ resolve ( text ) {
55
+ const record = data . get ( text ) ;
56
+ if ( record === undefined ) {
57
+ const newRecord = {
58
+ status : 'resolved' ,
59
+ value : text ,
60
+ cleanupScheduled : false ,
61
+ } ;
62
+ data . set ( text , newRecord ) ;
63
+ } else if ( record . status === 'pending' ) {
64
+ record . value . resolve ( ) ;
65
+ }
66
+ } ,
67
+ reject ( text , error ) {
68
+ const record = data . get ( text ) ;
69
+ if ( record === undefined ) {
70
+ const newRecord = {
71
+ status : 'rejected' ,
72
+ value : error ,
73
+ cleanupScheduled : false ,
74
+ } ;
75
+ data . set ( text , newRecord ) ;
76
+ } else if ( record . status === 'pending' ) {
77
+ record . value . reject ( ) ;
78
+ }
79
+ } ,
80
+ } ;
81
+ textCaches . push ( textCache ) ;
82
+ return textCache ;
83
+ } ) ;
84
+ }
85
+ } ) ;
83
86
84
87
function readText ( text ) {
85
88
const signal = getCacheSignal ( ) ;
86
- const textCache = getCacheForType ( createTextCache ) ;
89
+ const textCache = getTextCache ( ) ;
87
90
const record = textCache . data . get ( text ) ;
88
91
if ( record !== undefined ) {
89
92
if ( ! record . cleanupScheduled ) {
@@ -160,18 +163,18 @@ describe('ReactCache', () => {
160
163
161
164
function seedNextTextCache ( text ) {
162
165
if ( seededCache === null ) {
163
- seededCache = createTextCache ( ) ;
166
+ seededCache = getTextCache ( ) ;
164
167
}
165
168
seededCache . resolve ( text ) ;
166
169
}
167
170
168
171
function resolveMostRecentTextCache ( text ) {
169
- if ( caches . length === 0 ) {
172
+ if ( textCaches . length === 0 ) {
170
173
throw Error ( 'Cache does not exist.' ) ;
171
174
} else {
172
175
// Resolve the most recently created cache. An older cache can by
173
- // resolved with `caches [index].resolve(text)`.
174
- caches [ caches . length - 1 ] . resolve ( text ) ;
176
+ // resolved with `textCaches [index].resolve(text)`.
177
+ textCaches [ textCaches . length - 1 ] . resolve ( text ) ;
175
178
}
176
179
}
177
180
@@ -815,9 +818,18 @@ describe('ReactCache', () => {
815
818
816
819
// @gate experimental || www
817
820
test ( 'refresh a cache with seed data' , async ( ) => {
818
- let refresh ;
821
+ let refreshWithSeed ;
819
822
function App ( ) {
820
- refresh = useCacheRefresh ( ) ;
823
+ const refresh = useCacheRefresh ( ) ;
824
+ const [ seed , setSeed ] = useState ( { fn : null } ) ;
825
+ if ( seed . fn ) {
826
+ seed . fn ( ) ;
827
+ seed . fn = null ;
828
+ }
829
+ refreshWithSeed = fn => {
830
+ setSeed ( { fn} ) ;
831
+ refresh ( ) ;
832
+ } ;
821
833
return < AsyncText showVersion = { true } text = "A" /> ;
822
834
}
823
835
@@ -845,11 +857,14 @@ describe('ReactCache', () => {
845
857
await act ( async ( ) => {
846
858
// Refresh the cache with seeded data, like you would receive from a
847
859
// server mutation.
848
- // TODO: Seeding multiple typed caches . Should work by calling `refresh`
860
+ // TODO: Seeding multiple typed textCaches . Should work by calling `refresh`
849
861
// multiple times with different key/value pairs
850
- const cache = createTextCache ( ) ;
851
- cache . resolve ( 'A' ) ;
852
- startTransition ( ( ) => refresh ( createTextCache , cache ) ) ;
862
+ startTransition ( ( ) =>
863
+ refreshWithSeed ( ( ) => {
864
+ const textCache = getTextCache ( ) ;
865
+ textCache . resolve ( 'A' ) ;
866
+ } ) ,
867
+ ) ;
853
868
} ) ;
854
869
// The root should re-render without a cache miss.
855
870
// The cache is not cleared up yet, since it's still reference by the root
@@ -1624,4 +1639,152 @@ describe('ReactCache', () => {
1624
1639
expect ( Scheduler ) . toHaveYielded ( [ 'More' ] ) ;
1625
1640
expect ( root ) . toMatchRenderedOutput ( < div hidden = { true } > More</ div > ) ;
1626
1641
} ) ;
1642
+
1643
+ // @gate enableCache
1644
+ it ( 'cache objects and primitive arguments and a mix of them' , async ( ) => {
1645
+ const root = ReactNoop . createRoot ( ) ;
1646
+ const types = cache ( ( a , b ) => ( { a : typeof a , b : typeof b } ) ) ;
1647
+ function Print ( { a, b} ) {
1648
+ return types ( a , b ) . a + ' ' + types ( a , b ) . b + ' ' ;
1649
+ }
1650
+ function Same ( { a, b} ) {
1651
+ const x = types ( a , b ) ;
1652
+ const y = types ( a , b ) ;
1653
+ return ( x === y ) . toString ( ) + ' ' ;
1654
+ }
1655
+ function FlippedOrder ( { a, b} ) {
1656
+ return ( types ( a , b ) === types ( b , a ) ) . toString ( ) + ' ' ;
1657
+ }
1658
+ function FewerArgs ( { a, b} ) {
1659
+ return ( types ( a , b ) === types ( a ) ) . toString ( ) + ' ' ;
1660
+ }
1661
+ function MoreArgs ( { a, b} ) {
1662
+ return ( types ( a ) === types ( a , b ) ) . toString ( ) + ' ' ;
1663
+ }
1664
+ await act ( async ( ) => {
1665
+ root . render (
1666
+ < >
1667
+ < Print a = "e" b = "f" />
1668
+ < Same a = "a" b = "b" />
1669
+ < FlippedOrder a = "c" b = "d" />
1670
+ < FewerArgs a = "e" b = "f" />
1671
+ < MoreArgs a = "g" b = "h" />
1672
+ </ > ,
1673
+ ) ;
1674
+ } ) ;
1675
+ expect ( root ) . toMatchRenderedOutput ( 'string string true false false false ' ) ;
1676
+ await act ( async ( ) => {
1677
+ root . render (
1678
+ < >
1679
+ < Print a = "e" b = { null } />
1680
+ < Same a = "a" b = { null } />
1681
+ < FlippedOrder a = "c" b = { null } />
1682
+ < FewerArgs a = "e" b = { null } />
1683
+ < MoreArgs a = "g" b = { null } />
1684
+ </ > ,
1685
+ ) ;
1686
+ } ) ;
1687
+ expect ( root ) . toMatchRenderedOutput ( 'string object true false false false ' ) ;
1688
+ const obj = { } ;
1689
+ await act ( async ( ) => {
1690
+ root . render (
1691
+ < >
1692
+ < Print a = "e" b = { obj } />
1693
+ < Same a = "a" b = { obj } />
1694
+ < FlippedOrder a = "c" b = { obj } />
1695
+ < FewerArgs a = "e" b = { obj } />
1696
+ < MoreArgs a = "g" b = { obj } />
1697
+ </ > ,
1698
+ ) ;
1699
+ } ) ;
1700
+ expect ( root ) . toMatchRenderedOutput ( 'string object true false false false ' ) ;
1701
+ const sameObj = { } ;
1702
+ await act ( async ( ) => {
1703
+ root . render (
1704
+ < >
1705
+ < Print a = { sameObj } b = { sameObj } />
1706
+ < Same a = { sameObj } b = { sameObj } />
1707
+ < FlippedOrder a = { sameObj } b = { sameObj } />
1708
+ < FewerArgs a = { sameObj } b = { sameObj } />
1709
+ < MoreArgs a = { sameObj } b = { sameObj } />
1710
+ </ > ,
1711
+ ) ;
1712
+ } ) ;
1713
+ expect ( root ) . toMatchRenderedOutput ( 'object object true true false false ' ) ;
1714
+ const objA = { } ;
1715
+ const objB = { } ;
1716
+ await act ( async ( ) => {
1717
+ root . render (
1718
+ < >
1719
+ < Print a = { objA } b = { objB } />
1720
+ < Same a = { objA } b = { objB } />
1721
+ < FlippedOrder a = { objA } b = { objB } />
1722
+ < FewerArgs a = { objA } b = { objB } />
1723
+ < MoreArgs a = { objA } b = { objB } />
1724
+ </ > ,
1725
+ ) ;
1726
+ } ) ;
1727
+ expect ( root ) . toMatchRenderedOutput ( 'object object true false false false ' ) ;
1728
+ const sameSymbol = Symbol ( ) ;
1729
+ await act ( async ( ) => {
1730
+ root . render (
1731
+ < >
1732
+ < Print a = { sameSymbol } b = { sameSymbol } />
1733
+ < Same a = { sameSymbol } b = { sameSymbol } />
1734
+ < FlippedOrder a = { sameSymbol } b = { sameSymbol } />
1735
+ < FewerArgs a = { sameSymbol } b = { sameSymbol } />
1736
+ < MoreArgs a = { sameSymbol } b = { sameSymbol } />
1737
+ </ > ,
1738
+ ) ;
1739
+ } ) ;
1740
+ expect ( root ) . toMatchRenderedOutput ( 'symbol symbol true true false false ' ) ;
1741
+ const notANumber = + 'nan' ;
1742
+ await act ( async ( ) => {
1743
+ root . render (
1744
+ < >
1745
+ < Print a = { 1 } b = { notANumber } />
1746
+ < Same a = { 1 } b = { notANumber } />
1747
+ < FlippedOrder a = { 1 } b = { notANumber } />
1748
+ < FewerArgs a = { 1 } b = { notANumber } />
1749
+ < MoreArgs a = { 1 } b = { notANumber } />
1750
+ </ > ,
1751
+ ) ;
1752
+ } ) ;
1753
+ expect ( root ) . toMatchRenderedOutput ( 'number number true false false false ' ) ;
1754
+ } ) ;
1755
+
1756
+ // @gate enableCache
1757
+ it ( 'cached functions that throw should cache the error' , async ( ) => {
1758
+ const root = ReactNoop . createRoot ( ) ;
1759
+ const throws = cache ( v => {
1760
+ throw new Error ( v ) ;
1761
+ } ) ;
1762
+ let x ;
1763
+ let y ;
1764
+ let z ;
1765
+ function Test ( ) {
1766
+ try {
1767
+ throws ( 1 ) ;
1768
+ } catch ( e ) {
1769
+ x = e ;
1770
+ }
1771
+ try {
1772
+ throws ( 1 ) ;
1773
+ } catch ( e ) {
1774
+ y = e ;
1775
+ }
1776
+ try {
1777
+ throws ( 2 ) ;
1778
+ } catch ( e ) {
1779
+ z = e ;
1780
+ }
1781
+
1782
+ return 'Blank' ;
1783
+ }
1784
+ await act ( async ( ) => {
1785
+ root . render ( < Test /> ) ;
1786
+ } ) ;
1787
+ expect ( x ) . toBe ( y ) ;
1788
+ expect ( z ) . not . toBe ( x ) ;
1789
+ } ) ;
1627
1790
} ) ;
0 commit comments