@@ -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,66 @@ 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
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
- }
38
+ getTextCache = cache ( ( ) => {
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 textCache = seededCache ;
44
+ seededCache = null ;
45
+ return textCache ;
46
+ }
47
47
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 ( ) ;
64
- }
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
+ const data = new Map ( ) ;
49
+ const version = textCaches . length + 1 ;
50
+ const textCache = {
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 ( ) ;
64
+ }
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
+ textCaches . push ( textCache ) ;
81
+ return textCache ;
82
+ } ) ;
83
+ } ) ;
83
84
84
85
function readText ( text ) {
85
86
const signal = getCacheSignal ( ) ;
86
- const textCache = getCacheForType ( createTextCache ) ;
87
+ const textCache = getTextCache ( ) ;
87
88
const record = textCache . data . get ( text ) ;
88
89
if ( record !== undefined ) {
89
90
if ( ! record . cleanupScheduled ) {
@@ -160,18 +161,18 @@ describe('ReactCache', () => {
160
161
161
162
function seedNextTextCache ( text ) {
162
163
if ( seededCache === null ) {
163
- seededCache = createTextCache ( ) ;
164
+ seededCache = getTextCache ( ) ;
164
165
}
165
166
seededCache . resolve ( text ) ;
166
167
}
167
168
168
169
function resolveMostRecentTextCache ( text ) {
169
- if ( caches . length === 0 ) {
170
+ if ( textCaches . length === 0 ) {
170
171
throw Error ( 'Cache does not exist.' ) ;
171
172
} else {
172
173
// 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 ) ;
174
+ // resolved with `textCaches [index].resolve(text)`.
175
+ textCaches [ textCaches . length - 1 ] . resolve ( text ) ;
175
176
}
176
177
}
177
178
@@ -815,9 +816,18 @@ describe('ReactCache', () => {
815
816
816
817
// @gate experimental || www
817
818
test ( 'refresh a cache with seed data' , async ( ) => {
818
- let refresh ;
819
+ let refreshWithSeed ;
819
820
function App ( ) {
820
- refresh = useCacheRefresh ( ) ;
821
+ const refresh = useCacheRefresh ( ) ;
822
+ const [ seed , setSeed ] = useState ( { fn : null } ) ;
823
+ if ( seed . fn ) {
824
+ seed . fn ( ) ;
825
+ seed . fn = null ;
826
+ }
827
+ refreshWithSeed = fn => {
828
+ setSeed ( { fn} ) ;
829
+ refresh ( ) ;
830
+ } ;
821
831
return < AsyncText showVersion = { true } text = "A" /> ;
822
832
}
823
833
@@ -845,11 +855,14 @@ describe('ReactCache', () => {
845
855
await act ( async ( ) => {
846
856
// Refresh the cache with seeded data, like you would receive from a
847
857
// server mutation.
848
- // TODO: Seeding multiple typed caches . Should work by calling `refresh`
858
+ // TODO: Seeding multiple typed textCaches . Should work by calling `refresh`
849
859
// multiple times with different key/value pairs
850
- const cache = createTextCache ( ) ;
851
- cache . resolve ( 'A' ) ;
852
- startTransition ( ( ) => refresh ( createTextCache , cache ) ) ;
860
+ startTransition ( ( ) =>
861
+ refreshWithSeed ( ( ) => {
862
+ const textCache = getTextCache ( ) ;
863
+ textCache . resolve ( 'A' ) ;
864
+ } ) ,
865
+ ) ;
853
866
} ) ;
854
867
// The root should re-render without a cache miss.
855
868
// The cache is not cleared up yet, since it's still reference by the root
@@ -1624,4 +1637,152 @@ describe('ReactCache', () => {
1624
1637
expect ( Scheduler ) . toHaveYielded ( [ 'More' ] ) ;
1625
1638
expect ( root ) . toMatchRenderedOutput ( < div hidden = { true } > More</ div > ) ;
1626
1639
} ) ;
1640
+
1641
+ // @gate enableCache
1642
+ it ( 'cache objects and primitive arguments and a mix of them' , async ( ) => {
1643
+ const root = ReactNoop . createRoot ( ) ;
1644
+ const types = cache ( ( a , b ) => ( { a : typeof a , b : typeof b } ) ) ;
1645
+ function Print ( { a, b} ) {
1646
+ return types ( a , b ) . a + ' ' + types ( a , b ) . b + ' ' ;
1647
+ }
1648
+ function Same ( { a, b} ) {
1649
+ const x = types ( a , b ) ;
1650
+ const y = types ( a , b ) ;
1651
+ return ( x === y ) . toString ( ) + ' ' ;
1652
+ }
1653
+ function FlippedOrder ( { a, b} ) {
1654
+ return ( types ( a , b ) === types ( b , a ) ) . toString ( ) + ' ' ;
1655
+ }
1656
+ function FewerArgs ( { a, b} ) {
1657
+ return ( types ( a , b ) === types ( a ) ) . toString ( ) + ' ' ;
1658
+ }
1659
+ function MoreArgs ( { a, b} ) {
1660
+ return ( types ( a ) === types ( a , b ) ) . toString ( ) + ' ' ;
1661
+ }
1662
+ await act ( async ( ) => {
1663
+ root . render (
1664
+ < >
1665
+ < Print a = "e" b = "f" />
1666
+ < Same a = "a" b = "b" />
1667
+ < FlippedOrder a = "c" b = "d" />
1668
+ < FewerArgs a = "e" b = "f" />
1669
+ < MoreArgs a = "g" b = "h" />
1670
+ </ > ,
1671
+ ) ;
1672
+ } ) ;
1673
+ expect ( root ) . toMatchRenderedOutput ( 'string string true false false false ' ) ;
1674
+ await act ( async ( ) => {
1675
+ root . render (
1676
+ < >
1677
+ < Print a = "e" b = { null } />
1678
+ < Same a = "a" b = { null } />
1679
+ < FlippedOrder a = "c" b = { null } />
1680
+ < FewerArgs a = "e" b = { null } />
1681
+ < MoreArgs a = "g" b = { null } />
1682
+ </ > ,
1683
+ ) ;
1684
+ } ) ;
1685
+ expect ( root ) . toMatchRenderedOutput ( 'string object true false false false ' ) ;
1686
+ const obj = { } ;
1687
+ await act ( async ( ) => {
1688
+ root . render (
1689
+ < >
1690
+ < Print a = "e" b = { obj } />
1691
+ < Same a = "a" b = { obj } />
1692
+ < FlippedOrder a = "c" b = { obj } />
1693
+ < FewerArgs a = "e" b = { obj } />
1694
+ < MoreArgs a = "g" b = { obj } />
1695
+ </ > ,
1696
+ ) ;
1697
+ } ) ;
1698
+ expect ( root ) . toMatchRenderedOutput ( 'string object true false false false ' ) ;
1699
+ const sameObj = { } ;
1700
+ await act ( async ( ) => {
1701
+ root . render (
1702
+ < >
1703
+ < Print a = { sameObj } b = { sameObj } />
1704
+ < Same a = { sameObj } b = { sameObj } />
1705
+ < FlippedOrder a = { sameObj } b = { sameObj } />
1706
+ < FewerArgs a = { sameObj } b = { sameObj } />
1707
+ < MoreArgs a = { sameObj } b = { sameObj } />
1708
+ </ > ,
1709
+ ) ;
1710
+ } ) ;
1711
+ expect ( root ) . toMatchRenderedOutput ( 'object object true true false false ' ) ;
1712
+ const objA = { } ;
1713
+ const objB = { } ;
1714
+ await act ( async ( ) => {
1715
+ root . render (
1716
+ < >
1717
+ < Print a = { objA } b = { objB } />
1718
+ < Same a = { objA } b = { objB } />
1719
+ < FlippedOrder a = { objA } b = { objB } />
1720
+ < FewerArgs a = { objA } b = { objB } />
1721
+ < MoreArgs a = { objA } b = { objB } />
1722
+ </ > ,
1723
+ ) ;
1724
+ } ) ;
1725
+ expect ( root ) . toMatchRenderedOutput ( 'object object true false false false ' ) ;
1726
+ const sameSymbol = Symbol ( ) ;
1727
+ await act ( async ( ) => {
1728
+ root . render (
1729
+ < >
1730
+ < Print a = { sameSymbol } b = { sameSymbol } />
1731
+ < Same a = { sameSymbol } b = { sameSymbol } />
1732
+ < FlippedOrder a = { sameSymbol } b = { sameSymbol } />
1733
+ < FewerArgs a = { sameSymbol } b = { sameSymbol } />
1734
+ < MoreArgs a = { sameSymbol } b = { sameSymbol } />
1735
+ </ > ,
1736
+ ) ;
1737
+ } ) ;
1738
+ expect ( root ) . toMatchRenderedOutput ( 'symbol symbol true true false false ' ) ;
1739
+ const notANumber = + 'nan' ;
1740
+ await act ( async ( ) => {
1741
+ root . render (
1742
+ < >
1743
+ < Print a = { 1 } b = { notANumber } />
1744
+ < Same a = { 1 } b = { notANumber } />
1745
+ < FlippedOrder a = { 1 } b = { notANumber } />
1746
+ < FewerArgs a = { 1 } b = { notANumber } />
1747
+ < MoreArgs a = { 1 } b = { notANumber } />
1748
+ </ > ,
1749
+ ) ;
1750
+ } ) ;
1751
+ expect ( root ) . toMatchRenderedOutput ( 'number number true false false false ' ) ;
1752
+ } ) ;
1753
+
1754
+ // @gate enableCache
1755
+ it ( 'cached functions that throw should cache the error' , async ( ) => {
1756
+ const root = ReactNoop . createRoot ( ) ;
1757
+ const throws = cache ( v => {
1758
+ throw new Error ( v ) ;
1759
+ } ) ;
1760
+ let x ;
1761
+ let y ;
1762
+ let z ;
1763
+ function Test ( ) {
1764
+ try {
1765
+ throws ( 1 ) ;
1766
+ } catch ( e ) {
1767
+ x = e ;
1768
+ }
1769
+ try {
1770
+ throws ( 1 ) ;
1771
+ } catch ( e ) {
1772
+ y = e ;
1773
+ }
1774
+ try {
1775
+ throws ( 2 ) ;
1776
+ } catch ( e ) {
1777
+ z = e ;
1778
+ }
1779
+
1780
+ return 'Blank' ;
1781
+ }
1782
+ await act ( async ( ) => {
1783
+ root . render ( < Test /> ) ;
1784
+ } ) ;
1785
+ expect ( x ) . toBe ( y ) ;
1786
+ expect ( z ) . not . toBe ( x ) ;
1787
+ } ) ;
1627
1788
} ) ;
0 commit comments