From 0cb79f1a88a3a230de86321875a5690e6317d10a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Mon, 24 Jun 2024 15:55:24 +0100 Subject: [PATCH 1/2] Fix useOnyx selector return type --- lib/useOnyx.ts | 4 ++-- tests/unit/useOnyxTest.ts | 49 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/lib/useOnyx.ts b/lib/useOnyx.ts index 44d18086..ffbcdc80 100644 --- a/lib/useOnyx.ts +++ b/lib/useOnyx.ts @@ -4,7 +4,7 @@ import type {IsEqual} from 'type-fest'; import Onyx from './Onyx'; import OnyxCache from './OnyxCache'; import OnyxUtils from './OnyxUtils'; -import type {CollectionKeyBase, OnyxCollection, OnyxKey, OnyxValue, Selector} from './types'; +import type {CollectionKeyBase, OnyxCollection, OnyxEntry, OnyxKey, OnyxValue, Selector} from './types'; import useLiveRef from './useLiveRef'; import usePrevious from './usePrevious'; @@ -46,7 +46,7 @@ type UseOnyxOptions = BaseUseOnyxOptions & U type FetchStatus = 'loading' | 'loaded'; -type CachedValue = IsEqual> extends true ? TValue : TKey extends CollectionKeyBase ? NonNullable> : TValue; +type CachedValue = IsEqual> extends true ? TValue : TKey extends CollectionKeyBase ? OnyxCollection : OnyxEntry; type ResultMetadata = { status: FetchStatus; diff --git a/tests/unit/useOnyxTest.ts b/tests/unit/useOnyxTest.ts index 40847fb3..42f1c9f1 100644 --- a/tests/unit/useOnyxTest.ts +++ b/tests/unit/useOnyxTest.ts @@ -111,7 +111,7 @@ describe('useOnyx', () => { expect(result.current[1].status).toEqual('loaded'); }); - it('should initially return null and then return cached value after multiple merge operations', async () => { + it('should initially return undefined and then return cached value after multiple merge operations', async () => { Onyx.merge(ONYXKEYS.TEST_KEY, 'test1'); Onyx.merge(ONYXKEYS.TEST_KEY, 'test2'); Onyx.merge(ONYXKEYS.TEST_KEY, 'test3'); @@ -285,7 +285,7 @@ describe('useOnyx', () => { }); describe('initialValue', () => { - it('should return initial value from non-cached key and then return null', async () => { + it('should return initial value from non-cached key and then return undefined', async () => { const {result} = renderHook(() => useOnyx(ONYXKEYS.TEST_KEY, { initialValue: 'initial value', @@ -318,10 +318,30 @@ describe('useOnyx', () => { expect(result.current[0]).toEqual('test'); expect(result.current[1].status).toEqual('loaded'); }); + + it('should return initial value from cached key and then return selected data', async () => { + await StorageMock.setItem(ONYXKEYS.TEST_KEY, 'test'); + + const {result} = renderHook(() => + useOnyx(ONYXKEYS.TEST_KEY, { + initialValue: 'initial value', + // @ts-expect-error bypass + selector: (entry: OnyxEntry) => `${entry}_changed`, + }), + ); + + expect(result.current[0]).toEqual('initial value'); + expect(result.current[1].status).toEqual('loaded'); + + await act(async () => waitForPromisesToResolve()); + + expect(result.current[0]).toEqual('test_changed'); + expect(result.current[1].status).toEqual('loaded'); + }); }); describe('allowStaleData', () => { - it('should return null and loading state while we have pending merges for the key, and then return updated value and loaded state', async () => { + it('should return undefined and loading state while we have pending merges for the key, and then return updated value and loaded state', async () => { Onyx.set(ONYXKEYS.TEST_KEY, 'test1'); Onyx.merge(ONYXKEYS.TEST_KEY, 'test2'); @@ -339,6 +359,29 @@ describe('useOnyx', () => { expect(result.current[1].status).toEqual('loaded'); }); + it('should return undefined and loading state while we have pending merges for the key, and then return selected data and loaded state', async () => { + Onyx.set(ONYXKEYS.TEST_KEY, 'test1'); + + Onyx.merge(ONYXKEYS.TEST_KEY, 'test2'); + Onyx.merge(ONYXKEYS.TEST_KEY, 'test3'); + Onyx.merge(ONYXKEYS.TEST_KEY, 'test4'); + + const {result} = renderHook(() => + useOnyx(ONYXKEYS.TEST_KEY, { + // @ts-expect-error bypass + selector: (entry: OnyxEntry) => `${entry}_changed`, + }), + ); + + expect(result.current[0]).toBeUndefined(); + expect(result.current[1].status).toEqual('loading'); + + await act(async () => waitForPromisesToResolve()); + + expect(result.current[0]).toEqual('test4_changed'); + expect(result.current[1].status).toEqual('loaded'); + }); + it('should return initial value and loaded state while we have pending merges for the key, and then return updated value and loaded state', async () => { const {result} = renderHook(() => useOnyx(ONYXKEYS.TEST_KEY, { From 933a5d1f36dde1e3a691b016a0d7b3ee4d6d36d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Mon, 24 Jun 2024 16:21:56 +0100 Subject: [PATCH 2/2] Improve CachedValue type readability --- lib/useOnyx.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/useOnyx.ts b/lib/useOnyx.ts index ffbcdc80..e523eaff 100644 --- a/lib/useOnyx.ts +++ b/lib/useOnyx.ts @@ -46,7 +46,9 @@ type UseOnyxOptions = BaseUseOnyxOptions & U type FetchStatus = 'loading' | 'loaded'; -type CachedValue = IsEqual> extends true ? TValue : TKey extends CollectionKeyBase ? OnyxCollection : OnyxEntry; +type SelectedValue = TKey extends CollectionKeyBase ? OnyxCollection : OnyxEntry; + +type CachedValue = IsEqual> extends true ? TValue : SelectedValue; type ResultMetadata = { status: FetchStatus;