@@ -2,16 +2,17 @@ let React;
22let ReactNoop ;
33let Cache ;
44let getCacheSignal ;
5- let getCacheForType ;
65let Scheduler ;
76let act ;
87let Suspense ;
98let Offscreen ;
109let useCacheRefresh ;
1110let startTransition ;
1211let useState ;
12+ let cache ;
1313
14- let caches ;
14+ let getTextCache ;
15+ let textCaches ;
1516let seededCache ;
1617
1718describe ( 'ReactCache' , ( ) => {
@@ -24,66 +25,68 @@ describe('ReactCache', () => {
2425 Scheduler = require ( 'scheduler' ) ;
2526 act = require ( 'jest-react' ) . act ;
2627 Suspense = React . Suspense ;
28+ cache = React . experimental_cache ;
2729 Offscreen = React . unstable_Offscreen ;
2830 getCacheSignal = React . unstable_getCacheSignal ;
29- getCacheForType = React . unstable_getCacheForType ;
3031 useCacheRefresh = React . unstable_useCacheRefresh ;
3132 startTransition = React . startTransition ;
3233 useState = React . useState ;
3334
34- caches = [ ] ;
35+ textCaches = [ ] ;
3536 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- }
4737
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 ;
6447 }
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+ } ) ;
8386
8487 function readText ( text ) {
8588 const signal = getCacheSignal ( ) ;
86- const textCache = getCacheForType ( createTextCache ) ;
89+ const textCache = getTextCache ( ) ;
8790 const record = textCache . data . get ( text ) ;
8891 if ( record !== undefined ) {
8992 if ( ! record . cleanupScheduled ) {
@@ -160,18 +163,18 @@ describe('ReactCache', () => {
160163
161164 function seedNextTextCache ( text ) {
162165 if ( seededCache === null ) {
163- seededCache = createTextCache ( ) ;
166+ seededCache = getTextCache ( ) ;
164167 }
165168 seededCache . resolve ( text ) ;
166169 }
167170
168171 function resolveMostRecentTextCache ( text ) {
169- if ( caches . length === 0 ) {
172+ if ( textCaches . length === 0 ) {
170173 throw Error ( 'Cache does not exist.' ) ;
171174 } else {
172175 // 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 ) ;
175178 }
176179 }
177180
@@ -815,9 +818,18 @@ describe('ReactCache', () => {
815818
816819 // @gate experimental || www
817820 test ( 'refresh a cache with seed data' , async ( ) => {
818- let refresh ;
821+ let refreshWithSeed ;
819822 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+ } ;
821833 return < AsyncText showVersion = { true } text = "A" /> ;
822834 }
823835
@@ -845,11 +857,14 @@ describe('ReactCache', () => {
845857 await act ( async ( ) => {
846858 // Refresh the cache with seeded data, like you would receive from a
847859 // server mutation.
848- // TODO: Seeding multiple typed caches . Should work by calling `refresh`
860+ // TODO: Seeding multiple typed textCaches . Should work by calling `refresh`
849861 // 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+ ) ;
853868 } ) ;
854869 // The root should re-render without a cache miss.
855870 // The cache is not cleared up yet, since it's still reference by the root
@@ -1624,4 +1639,152 @@ describe('ReactCache', () => {
16241639 expect ( Scheduler ) . toHaveYielded ( [ 'More' ] ) ;
16251640 expect ( root ) . toMatchRenderedOutput ( < div hidden = { true } > More</ div > ) ;
16261641 } ) ;
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+ } ) ;
16271790} ) ;
0 commit comments