Repository navigation
Skip unchanged cached subtrees when removing nested nulls #856
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
base: main
Are you sure you want to change the base?
Changes from all commits
41d640a
6839ba1
6ee53a2
7aecfc0
b6722ef
94edaee
65f5f9d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -64,13 +64,15 @@ function init({ | |
| const individual: Array<[OnyxKey, OnyxEntry<KeyValueMapping[OnyxKey]>]> = []; | ||
| const collectionBatches = new Map<string, {partial: NonUndefined<OnyxCollection<KeyValueMapping[OnyxKey]>>; previous: NonUndefined<OnyxCollection<KeyValueMapping[OnyxKey]>>}>(); | ||
|
|
||
| for (const [key, value] of pairs) { | ||
| for (const [key, valueFromStorage] of pairs) { | ||
| // RAM-only keys should never sync from storage as they may have stale persisted data | ||
| // from before the key was migrated to RAM-only. | ||
| if (OnyxKeys.isRamOnlyKey(key)) { | ||
| continue; | ||
| } | ||
|
|
||
| const value = utils.needsNormalization(valueFromStorage) ? utils.removeNestedNullValues(valueFromStorage) : valueFromStorage; | ||
|
|
||
| const collectionKey = OnyxKeys.getCollectionKey(key); | ||
| const isCollectionMember = !!collectionKey && OnyxKeys.isCollectionMemberKey(collectionKey, key); | ||
|
|
||
|
|
@@ -489,7 +491,7 @@ function clear(keysToPreserve: OnyxKey[] = []): Promise<void> { | |
| // since collection key subscribers need to be updated differently | ||
| if (!isKeyToPreserve) { | ||
| const oldValue = cache.get(key); | ||
| const newValue = defaultKeyStates[key] ?? null; | ||
| const newValue = utils.removeNestedNullValues(defaultKeyStates[key] ?? null); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NAB:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NAB: No test covers this either. Reverting to |
||
| if (newValue !== oldValue) { | ||
| cache.set(key, newValue); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,4 +1,5 @@ | ||||||
| import type {OnyxInput, OnyxKey} from './types'; | ||||||
| import * as Logger from './Logger'; | ||||||
|
|
||||||
| type EmptyObject = Record<string, never>; | ||||||
| type EmptyValue = EmptyObject | null | undefined; | ||||||
|
|
@@ -156,7 +157,8 @@ function mergeObject<TObject extends Record<string, unknown>>( | |||||
| // of the merged object only. | ||||||
| const sourcePropertyWithoutMark = {...sourceProperty}; | ||||||
| delete sourcePropertyWithoutMark.ONYX_INTERNALS__REPLACE_OBJECT_MARK; | ||||||
| destination[key] = sourcePropertyWithoutMark; | ||||||
| // Built in "mark" mode, so it can still hold nested nulls. | ||||||
| destination[key] = options.shouldRemoveNestedNulls ? removeNestedNullValues(sourcePropertyWithoutMark) : sourcePropertyWithoutMark; | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -226,12 +228,25 @@ function needsNormalization(value: unknown): boolean { | |||||
| return false; | ||||||
| } | ||||||
|
|
||||||
| /** Deep removes the nested null values from the given value. Returns the original reference if no nulls were found. */ | ||||||
| function removeNestedNullValues<TValue extends OnyxInput<OnyxKey> | null>(value: TValue): TValue { | ||||||
| /** | ||||||
| * Deep removes the nested null values from the given value. Returns the original reference if no nulls were found. | ||||||
| * | ||||||
| * Nested objects that are the same reference as the object at the same path in `nullFreeReference` are returned as is, | ||||||
| * without being traversed. Pass the live cache value here, since cached values never hold nested nulls. | ||||||
| */ | ||||||
|
Comment on lines
+231
to
+236
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NAB: arrays are never cleaned (neither |
||||||
| function removeNestedNullValues<TValue extends OnyxInput<OnyxKey> | null>(value: TValue, nullFreeReference?: unknown): TValue { | ||||||
| if (value === null || value === undefined || typeof value !== 'object' || Array.isArray(value)) { | ||||||
| return value; | ||||||
| } | ||||||
|
|
||||||
| if (value === nullFreeReference) { | ||||||
| if (process.env.NODE_ENV === 'development' && needsNormalization(value)) { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
We should log during tests too, no? |
||||||
| Logger.logAlert('removeNestedNullValues skipped a cached subtree that holds nested null values. Onyx values must not be mutated in place.'); | ||||||
| } | ||||||
| return value; | ||||||
| } | ||||||
|
|
||||||
| const reference = nullFreeReference !== null && typeof nullFreeReference === 'object' && !Array.isArray(nullFreeReference) ? (nullFreeReference as Record<string, unknown>) : undefined; | ||||||
| let hasChanged = false; | ||||||
| const result: Record<string, unknown> = {}; | ||||||
|
|
||||||
|
|
@@ -245,7 +260,7 @@ function removeNestedNullValues<TValue extends OnyxInput<OnyxKey> | null>(value: | |||||
| } | ||||||
|
|
||||||
| if (typeof propertyValue === 'object' && !Array.isArray(propertyValue)) { | ||||||
| const cleaned = removeNestedNullValues(propertyValue); | ||||||
| const cleaned = removeNestedNullValues(propertyValue, reference?.[key]); | ||||||
| if (cleaned !== propertyValue) { | ||||||
| hasChanged = true; | ||||||
| } | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAB: No test covers this fix. If this line is changed back to c
onst value = valueFromStorage;, all tests still pass.