Skip to content

Commit

Permalink
Merge pull request Expensify#573 from margelo/fix/reset-default-value…
Browse files Browse the repository at this point in the history
…s-on-clear

fix: reset keys to initial state if they were set to null
  • Loading branch information
MonilBhavsar authored Aug 1, 2024
2 parents b2e5384 + b7d5048 commit e68b548
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 18 deletions.
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

0 comments on commit e68b548

Please sign in to comment.