Coming from here
Background
A component reads an Onyx collection — the set of records sharing a key prefix, e.g. every report under report_ — through either useOnyx or the imperative Onyx.connect/Onyx.connectWithoutView. Such a subscription delivers updates in one of two modes:
Per-member (the default): the callback fires once per member — on the initial connection, once for each member already present in the collection; on each later write, once for every member whose value changed — as callback(memberValue, memberKey).
Whole-collection (waitForCollectionCallback: true): the callback fires a single time with the entire collection object — callback(collection, collectionKey).
Both modes are backed by the same data: Onyx keeps a frozen object for each collection in its cache, in which members that didn't change retain the same reference across writes. Across all of expensify-app, only three consumers still use per-member mode (Report/index.ts, replaceOptimisticReportWithActualReport.ts, Transaction.ts))); every other collection subscriber uses whole-collection mode.
Problem
When a collection subscription can deliver its data two ways — per-member (the default) or as the whole collection — if an engineer writes, reviews, or changes a collection subscription or the notify code behind it, then they must understand and keep both firing behaviors working even though the two carry the same data. Only three consumers use per-member mode, and each just rebuilds the whole collection it withholds — one of them incorrectly — so the mode adds permanent cost while giving its users nothing whole-collection mode doesn't.
Solution
Remove the waitForCollectionCallback option and the per-member dispatch path, leaving a single delivery mode: a collection subscription always hands back the whole collection object in one callback. This collapses the two-mode choice every collection subscription had to make into one and deletes the dedicated per-member branch from the notification path, so neither new subscriptions nor future changes to the notify code have to reason about it.
Nothing is lost. The collection object already carries every member, so a consumer that needs to know which members changed compares the new object against the previous one — members that didn't change are reference-equal, so the changed set falls out in O(members), with an O(1) check per member. Because per-member mode delivered no information the whole-collection object lacks, it is a strict subset and can be removed outright rather than preserved.
All the three remaining per-member consumers maintain a module-level cache of an entire collection, rebuilt from the per-member callback — exactly what whole-collection mode delivers in one call. Each switches to whole-collection mode and reads the collection object directly:
- Report/index.ts and replaceOptimisticReportWithActualReport.ts subscribe to COLLECTION.REPORT_ACTIONS and accumulate allReportActions[reportID] = actions per member. Both become a single assignment from the collection object, which is already keyed by member (report) ID.
- Transaction.ts subscribes to COLLECTION.TRANSACTION_VIOLATIONS and assigns each fired member into a flat variable (allTransactionViolations = val ?? []), so only the last member to fire survived — a latent bug. Reading the whole collection object instead fixes it.
A consumer that truly needed per-member granularity could recover it by diffing the new collection object against the previous one (reference-equal members fall out in O(members), O(1) each), but none of these three do — they all want the whole collection.
Issue Owner
Current Issue Owner: @fabioh8010
Upwork Automation - Do Not Edit
Coming from here
Background
A component reads an Onyx collection — the set of records sharing a key prefix, e.g. every report under report_ — through either useOnyx or the imperative Onyx.connect/Onyx.connectWithoutView. Such a subscription delivers updates in one of two modes:
Per-member (the default): the callback fires once per member — on the initial connection, once for each member already present in the collection; on each later write, once for every member whose value changed — as callback(memberValue, memberKey).
Whole-collection (waitForCollectionCallback: true): the callback fires a single time with the entire collection object — callback(collection, collectionKey).
Both modes are backed by the same data: Onyx keeps a frozen object for each collection in its cache, in which members that didn't change retain the same reference across writes. Across all of expensify-app, only three consumers still use per-member mode (Report/index.ts, replaceOptimisticReportWithActualReport.ts, Transaction.ts))); every other collection subscriber uses whole-collection mode.
Problem
When a collection subscription can deliver its data two ways — per-member (the default) or as the whole collection — if an engineer writes, reviews, or changes a collection subscription or the notify code behind it, then they must understand and keep both firing behaviors working even though the two carry the same data. Only three consumers use per-member mode, and each just rebuilds the whole collection it withholds — one of them incorrectly — so the mode adds permanent cost while giving its users nothing whole-collection mode doesn't.
Solution
Remove the waitForCollectionCallback option and the per-member dispatch path, leaving a single delivery mode: a collection subscription always hands back the whole collection object in one callback. This collapses the two-mode choice every collection subscription had to make into one and deletes the dedicated per-member branch from the notification path, so neither new subscriptions nor future changes to the notify code have to reason about it.
Nothing is lost. The collection object already carries every member, so a consumer that needs to know which members changed compares the new object against the previous one — members that didn't change are reference-equal, so the changed set falls out in O(members), with an O(1) check per member. Because per-member mode delivered no information the whole-collection object lacks, it is a strict subset and can be removed outright rather than preserved.
All the three remaining per-member consumers maintain a module-level cache of an entire collection, rebuilt from the per-member callback — exactly what whole-collection mode delivers in one call. Each switches to whole-collection mode and reads the collection object directly:
A consumer that truly needed per-member granularity could recover it by diffing the new collection object against the previous one (reference-equal members fall out in O(members), O(1) each), but none of these three do — they all want the whole collection.
Issue Owner
Current Issue Owner: @fabioh8010Upwork Automation - Do Not Edit