diff --git a/lib/useOnyx.ts b/lib/useOnyx.ts index 80c14e485..c906040f6 100644 --- a/lib/useOnyx.ts +++ b/lib/useOnyx.ts @@ -108,6 +108,12 @@ function useOnyx>(key: TKey if (!shouldGetCachedValueRef.current) { const cachedResult = onyxSnapshotCache.getCachedResult>(key, cacheKey); if (cachedResult !== undefined) { + // The slot is shared by all subscribers of the same (key, selector) pair, so it can hold a content-equal + // result computed by another subscriber. Keep our own result then, otherwise we would needlessly change + // this hook's result identity and re-render its consumer. + if (cachedResult !== resultRef.current && memoizedShallowEqual(cachedResult[0], resultRef.current[0]) && cachedResult[1].status === resultRef.current[1].status) { + return resultRef.current; + } resultRef.current = cachedResult; return cachedResult; } diff --git a/tests/unit/useOnyxTest.ts b/tests/unit/useOnyxTest.ts index 385e285d9..837dcbdee 100644 --- a/tests/unit/useOnyxTest.ts +++ b/tests/unit/useOnyxTest.ts @@ -574,6 +574,28 @@ describe('useOnyx', () => { expect(oldResult).toBe(result.current); }); + it('should keep result identity when a new subscriber with the same key and selector mounts', async () => { + Onyx.set(ONYXKEYS.TEST_KEY, {id: 'test_id', name: 'test_name'}); + + const selector = ((entry: OnyxEntry<{id: string; name: string}>) => ({id: entry?.id})) as UseOnyxSelector; + + const {result, rerender} = renderHook(() => useOnyx(ONYXKEYS.TEST_KEY, {selector})); + + await act(async () => waitForPromisesToResolve()); + + const oldResult = result.current; + + // A subscriber that mounts later computes its own content-equal selector output and publishes it into the shared snapshot cache slot. + renderHook(() => useOnyx(ONYXKEYS.TEST_KEY, {selector})); + + await act(async () => waitForPromisesToResolve()); + + rerender(undefined); + + // must be the same reference — the new subscriber's content-equal result must not replace it + expect(result.current).toBe(oldResult); + }); + it('should always use the current selector reference to return new data', async () => { Onyx.set(ONYXKEYS.TEST_KEY, {id: 'test_id', name: 'test_name'});