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

fix: reset keys to initial state if they were set to null #573

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
12 changes: 8 additions & 4 deletions lib/Onyx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,22 +554,27 @@ function mergeCollection<TKey extends CollectionKeyBase, TMap>(collectionKey: TK
* @param keysToPreserve is a list of ONYXKEYS that should not be cleared with the rest of the data
*/
function clear(keysToPreserve: OnyxKey[] = []): Promise<void> {
const defaultKeyStates = OnyxUtils.getDefaultKeyStates();
const initialKeys = Object.keys(defaultKeyStates);

return OnyxUtils.getAllKeys()
.then((keys) => {
.then((cachedKeys) => {
cache.clearNullishStorageKeys();

const keysToBeClearedFromStorage: OnyxKey[] = [];
const keyValuesToResetAsCollection: Record<OnyxKey, OnyxCollection<KeyValueMapping[OnyxKey]>> = {};
const keyValuesToResetIndividually: KeyValueMapping = {};

const allKeys = new Set([...cachedKeys, ...initialKeys]);

// The only keys that should not be cleared are:
// 1. Anything specifically passed in keysToPreserve (because some keys like language preferences, offline
// status, or activeClients need to remain in Onyx even when signed out)
// 2. Any keys with a default state (because they need to remain in Onyx as their default, and setting them
// to null would cause unknown behavior)
keys.forEach((key) => {
// 2.1 However, if a default key was explicitly set to null, we need to reset it to the default value
allKeys.forEach((key) => {
const isKeyToPreserve = keysToPreserve.includes(key);
const defaultKeyStates = OnyxUtils.getDefaultKeyStates();
const isDefaultKey = key in defaultKeyStates;

// If the key is being removed or reset to default:
Expand Down Expand Up @@ -611,7 +616,6 @@ function clear(keysToPreserve: OnyxKey[] = []): Promise<void> {
updatePromises.push(OnyxUtils.scheduleNotifyCollectionSubscribers(key, value));
});

const defaultKeyStates = OnyxUtils.getDefaultKeyStates();
const defaultKeyValuePairs = Object.entries(
Object.keys(defaultKeyStates)
.filter((key) => !keysToPreserve.includes(key))
Expand Down
33 changes: 19 additions & 14 deletions tests/unit/onyxTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,23 @@ describe('Onyx', () => {
expect(keys.has(ONYX_KEYS.OTHER_TEST)).toBe(true);
return Onyx.set(ONYX_KEYS.OTHER_TEST, null);
})
// Checks if cache value is removed.
.then(() => {
// Checks if cache value is removed.
expect(cache.getAllKeys().size).toBe(0);

// When cache keys length is 0, we fetch the keys from storage.
expect(cache.get(ONYX_KEYS.OTHER_TEST)).toBeUndefined();
return OnyxUtils.getAllKeys();
})
.then((keys) => {
expect(keys.has(ONYX_KEYS.OTHER_TEST)).toBe(false);
}));

it('should restore a key with initial state if the key was set to null and Onyx.clear() is called', () =>
Onyx.set(ONYX_KEYS.OTHER_TEST, 42)
.then(() => Onyx.set(ONYX_KEYS.OTHER_TEST, null))
.then(() => Onyx.clear())
.then(() => {
expect(cache.get(ONYX_KEYS.OTHER_TEST)).toBe(42);
}));

it('should set a simple key', () => {
let testKeyValue: unknown;

Expand Down Expand Up @@ -167,32 +173,31 @@ describe('Onyx', () => {
},
});

let otherTestValue: unknown;
const mockCallback = jest.fn((value) => {
otherTestValue = value;
});
const mockCallback = jest.fn();
const otherTestConnectionID = Onyx.connect({
key: ONYX_KEYS.OTHER_TEST,
callback: mockCallback,
});

return waitForPromisesToResolve()
.then(() => {
expect(mockCallback).toHaveBeenCalledTimes(1);
expect(mockCallback).toHaveBeenCalledWith(42, ONYX_KEYS.OTHER_TEST);
mockCallback.mockClear();
})
.then(() => Onyx.set(ONYX_KEYS.TEST_KEY, 'test'))
.then(() => {
expect(testKeyValue).toBe('test');
mockCallback.mockReset();
return Onyx.clear().then(waitForPromisesToResolve);
return Onyx.clear();
})
.then(() => waitForPromisesToResolve())
.then(() => {
// Test key should be cleared
expect(testKeyValue).toBeUndefined();

// Expect that the connection to a key with a default value wasn't cleared
// Expect that the connection to a key with a default value that wasn't changed is not called on clear
expect(mockCallback).toHaveBeenCalledTimes(0);

// Other test key should be returned to its default state
expect(otherTestValue).toBe(42);

return Onyx.disconnect(otherTestConnectionID);
});
});
Expand Down
Loading