@@ -1271,4 +1271,73 @@ describe('useOnyx', () => {
12711271 expect ( result . current [ 1 ] . status ) . toEqual ( 'loaded' ) ;
12721272 } ) ;
12731273 } ) ;
1274+
1275+ // Regression coverage for Expensify/App#87850 ("[Onyx] Fix extra mount render introduced in useOnyx v3.0.59").
1276+ // The bug: `subscribe` unconditionally reset `resultRef.current` to a fresh tuple, including on initial mount.
1277+ // `useSyncExternalStore` then observed a different snapshot reference post-subscribe and scheduled an extra
1278+ // render per `useOnyx` hook. The fix guards the reset behind `hasMountedRef` so it only runs on re-subscription.
1279+ describe ( 'initial mount render count' , ( ) => {
1280+ it ( 'should render only once when the key has a value already in Onyx cache' , async ( ) => {
1281+ await Onyx . set ( ONYXKEYS . TEST_KEY , 'cached_value' ) ;
1282+
1283+ let renderCount = 0 ;
1284+ const { result} = renderHook ( ( ) => {
1285+ renderCount ++ ;
1286+ return useOnyx ( ONYXKEYS . TEST_KEY ) ;
1287+ } ) ;
1288+
1289+ await act ( async ( ) => waitForPromisesToResolve ( ) ) ;
1290+
1291+ expect ( result . current [ 0 ] ) . toEqual ( 'cached_value' ) ;
1292+ expect ( result . current [ 1 ] . status ) . toEqual ( 'loaded' ) ;
1293+ // A single render — no extra render caused by subscribe resetting state on initial mount.
1294+ expect ( renderCount ) . toBe ( 1 ) ;
1295+ } ) ;
1296+
1297+ it ( 'should render exactly twice (loading → loaded) when the key is not cached' , async ( ) => {
1298+ let renderCount = 0 ;
1299+ const { result} = renderHook ( ( ) => {
1300+ renderCount ++ ;
1301+ return useOnyx ( ONYXKEYS . TEST_KEY ) ;
1302+ } ) ;
1303+
1304+ await act ( async ( ) => waitForPromisesToResolve ( ) ) ;
1305+
1306+ expect ( result . current [ 0 ] ) . toBeUndefined ( ) ;
1307+ expect ( result . current [ 1 ] . status ) . toEqual ( 'loaded' ) ;
1308+ // Exactly two renders: initial 'loading' + transition to 'loaded' after the connection callback fires.
1309+ // If the regression returns, a third render sneaks in from the subscribe-time state reset.
1310+ expect ( renderCount ) . toBe ( 2 ) ;
1311+ } ) ;
1312+
1313+ it ( 'should render exactly twice when the key value is only present in storage' , async ( ) => {
1314+ await StorageMock . setItem ( ONYXKEYS . TEST_KEY , 'storage_value' ) ;
1315+
1316+ let renderCount = 0 ;
1317+ const { result} = renderHook ( ( ) => {
1318+ renderCount ++ ;
1319+ return useOnyx ( ONYXKEYS . TEST_KEY ) ;
1320+ } ) ;
1321+
1322+ await act ( async ( ) => waitForPromisesToResolve ( ) ) ;
1323+
1324+ expect ( result . current [ 0 ] ) . toEqual ( 'storage_value' ) ;
1325+ expect ( result . current [ 1 ] . status ) . toEqual ( 'loaded' ) ;
1326+ expect ( renderCount ) . toBe ( 2 ) ;
1327+ } ) ;
1328+
1329+ it ( 'should render exactly twice for a non-cached collection member key' , async ( ) => {
1330+ let renderCount = 0 ;
1331+ const { result} = renderHook ( ( ) => {
1332+ renderCount ++ ;
1333+ return useOnyx ( `${ ONYXKEYS . COLLECTION . TEST_KEY } 1` ) ;
1334+ } ) ;
1335+
1336+ await act ( async ( ) => waitForPromisesToResolve ( ) ) ;
1337+
1338+ expect ( result . current [ 0 ] ) . toBeUndefined ( ) ;
1339+ expect ( result . current [ 1 ] . status ) . toEqual ( 'loaded' ) ;
1340+ expect ( renderCount ) . toBe ( 2 ) ;
1341+ } ) ;
1342+ } ) ;
12741343} ) ;
0 commit comments