Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[No QA] Fix useOnyx selector return type #564

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions lib/useOnyx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -46,7 +46,9 @@ type UseOnyxOptions<TKey extends OnyxKey, TReturnValue> = BaseUseOnyxOptions & U

type FetchStatus = 'loading' | 'loaded';

type CachedValue<TKey extends OnyxKey, TValue> = IsEqual<TValue, OnyxValue<TKey>> extends true ? TValue : TKey extends CollectionKeyBase ? NonNullable<OnyxCollection<TValue>> : TValue;
type SelectedValue<TKey, TValue> = TKey extends CollectionKeyBase ? OnyxCollection<TValue> : OnyxEntry<TValue>;

type CachedValue<TKey extends OnyxKey, TValue> = IsEqual<TValue, OnyxValue<TKey>> extends true ? TValue : SelectedValue<TKey, TValue>;

type ResultMetadata = {
status: FetchStatus;
Expand Down
49 changes: 46 additions & 3 deletions tests/unit/useOnyxTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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<string>) => `${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');
Expand All @@ -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<string>) => `${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, {
Expand Down
Loading