Problem
When a large single Onyx key updates (e.g. personalDetailsList with ~18k members), every subscribed useOnyx hook independently runs shallowEqual(previousValue, newValue) inside getSnapshot() to decide whether to re-render. All hooks compare the exact same two object references yet each pays for its own full O(keys) walk — measured as 29 full walks and ~530ms blocked JS on a single update with ~200 subscribers.
Solution
Memoize shallowEqual verdicts by the identity of the compared object pair using nested WeakMaps. The first hook comparing a given (prev, next) pair pays for the real walk and stores the verdict; every other hook comparing the same pair resolves in O(1). Non-object inputs bypass the memo. No behavior change — stale entries are unreachable and garbage-collected with the old objects.
PR
Expensify/react-native-onyx#810
Issue Owner
Current Issue Owner: @abzokhattab
Problem
When a large single Onyx key updates (e.g.
personalDetailsListwith ~18k members), every subscribeduseOnyxhook independently runsshallowEqual(previousValue, newValue)insidegetSnapshot()to decide whether to re-render. All hooks compare the exact same two object references yet each pays for its own full O(keys) walk — measured as 29 full walks and ~530ms blocked JS on a single update with ~200 subscribers.Solution
Memoize
shallowEqualverdicts by the identity of the compared object pair using nestedWeakMaps. The first hook comparing a given(prev, next)pair pays for the real walk and stores the verdict; every other hook comparing the same pair resolves in O(1). Non-object inputs bypass the memo. No behavior change — stale entries are unreachable and garbage-collected with the old objects.PR
Expensify/react-native-onyx#810
Issue Owner
Current Issue Owner: @abzokhattab