From 24291369d8a975500b52090426fca2f0671af168 Mon Sep 17 00:00:00 2001 From: Hubert Sosinski Date: Mon, 2 Mar 2026 11:19:27 +0100 Subject: [PATCH 1/4] skip reportAttributes full recompute on initial locale load --- .../OnyxDerived/configs/reportAttributes.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts index 7be982ceb0080..4e120f1b5fa8e 100644 --- a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts +++ b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts @@ -8,7 +8,8 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, ReportAttributesDerivedValue} from '@src/types/onyx'; -let isFullyComputed = false; +let isFullyComputed = true; +let previousLocale: string | undefined; let previousDisplayNames: Record = {}; let previousPersonalDetails: OnyxEntry | undefined; @@ -57,7 +58,6 @@ const checkDisplayNamesChanged = (personalDetails: OnyxEntry Date: Mon, 2 Mar 2026 12:39:43 +0100 Subject: [PATCH 2/4] simplify locale change detection using currentValue?.locale --- .../OnyxDerived/configs/reportAttributes.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts index 4e120f1b5fa8e..f71131a9dd5f8 100644 --- a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts +++ b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts @@ -8,8 +8,10 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, ReportAttributesDerivedValue} from '@src/types/onyx'; +// Initialized to `true` because `reportAttributes` is persisted to disk and restored on startup, +// so the cached value is already valid. Starting `false` would trigger an unnecessary full recompute +// of all reports on the first compute call before any data has actually changed. let isFullyComputed = true; -let previousLocale: string | undefined; let previousDisplayNames: Record = {}; let previousPersonalDetails: OnyxEntry | undefined; @@ -88,18 +90,12 @@ export default createOnyxDerivedValueConfig({ // if any of those keys changed, reset the isFullyComputed flag to recompute all reports // we need to recompute all report attributes on locale change because the report names are locale dependent. - // We skip the reset on the very first locale load (previousLocale undefined) because the locale is already - // set correctly before any data batch arrives — report names will be computed in the right locale regardless. - if ((hasKeyTriggeredCompute(ONYXKEYS.NVP_PREFERRED_LOCALE, sourceValues) && !!previousLocale) || displayNamesChanged) { + // We compare preferredLocale against currentValue?.locale so that the first locale load on startup + // (where both equal the same persisted value) does not trigger an unnecessary full recompute. + if ((hasKeyTriggeredCompute(ONYXKEYS.NVP_PREFERRED_LOCALE, sourceValues) && preferredLocale !== currentValue?.locale) || displayNamesChanged) { isFullyComputed = false; } - // Only update previousLocale when we have an actual value, to avoid a null overwrite - // masking a subsequent real locale change. - if (preferredLocale) { - previousLocale = preferredLocale; - } - // if we already computed the report attributes and there is no new reports data, return the current value if ((isFullyComputed && !sourceValues) || !reports) { return currentValue ?? {reports: {}, locale: null}; From 18f99e08c398795b8866c6308a617c1edf9c24ca Mon Sep 17 00:00:00 2001 From: Hubert Sosinski Date: Fri, 6 Mar 2026 09:00:07 +0100 Subject: [PATCH 3/4] replace isFullyComputed flag with currentValue-based incremental check --- .../OnyxDerived/configs/reportAttributes.ts | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts index f71131a9dd5f8..ee46eac71df10 100644 --- a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts +++ b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts @@ -8,10 +8,6 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, ReportAttributesDerivedValue} from '@src/types/onyx'; -// Initialized to `true` because `reportAttributes` is persisted to disk and restored on startup, -// so the cached value is already valid. Starting `false` would trigger an unnecessary full recompute -// of all reports on the first compute call before any data has actually changed. -let isFullyComputed = true; let previousDisplayNames: Record = {}; let previousPersonalDetails: OnyxEntry | undefined; @@ -88,16 +84,17 @@ export default createOnyxDerivedValueConfig({ previousPersonalDetails = undefined; } - // if any of those keys changed, reset the isFullyComputed flag to recompute all reports - // we need to recompute all report attributes on locale change because the report names are locale dependent. + // A full recompute is needed when locale changes (report names are locale-dependent) or display names change. // We compare preferredLocale against currentValue?.locale so that the first locale load on startup // (where both equal the same persisted value) does not trigger an unnecessary full recompute. - if ((hasKeyTriggeredCompute(ONYXKEYS.NVP_PREFERRED_LOCALE, sourceValues) && preferredLocale !== currentValue?.locale) || displayNamesChanged) { - isFullyComputed = false; - } + const needsFullRecompute = (hasKeyTriggeredCompute(ONYXKEYS.NVP_PREFERRED_LOCALE, sourceValues) && preferredLocale !== currentValue?.locale) || displayNamesChanged; + + // Use incremental updates when currentValue is already populated and no full recompute is required. + // If currentValue has no reports (fresh install or cleared storage), fall back to a full scan. + const useIncrementalUpdates = !!currentValue?.reports && Object.keys(currentValue.reports).length > 0 && !needsFullRecompute; // if we already computed the report attributes and there is no new reports data, return the current value - if ((isFullyComputed && !sourceValues) || !reports) { + if ((useIncrementalUpdates && !sourceValues) || !reports) { return currentValue ?? {reports: {}, locale: null}; } @@ -133,7 +130,7 @@ export default createOnyxDerivedValueConfig({ ...Array.from(reportUpdatesRelatedToReportActions), ]; - if (isFullyComputed) { + if (useIncrementalUpdates) { // if there are report-related updates, iterate over the updates if (updates.length > 0 || !!transactionsUpdates || !!transactionViolationsUpdates) { if (updates.length > 0) { @@ -179,6 +176,7 @@ export default createOnyxDerivedValueConfig({ return currentValue ?? {reports: {}, locale: null}; } } + const reportAttributes = dataToIterate.reduce((acc, key) => { // source value sends partial data, so we need an entire report object to do computations const report = reports[key]; @@ -251,11 +249,6 @@ export default createOnyxDerivedValueConfig({ reportAttributes[chatReportID].brickRoadStatus = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; } - // mark the report attributes as fully computed after first iteration to avoid unnecessary recomputation on all objects - if (!Object.keys(reportUpdates).length && Object.keys(reports ?? {}).length > 0 && !isFullyComputed) { - isFullyComputed = true; - } - return { reports: reportAttributes, locale: preferredLocale ?? null, From e03c025b975336d2ba71e1fcb08c1a1d019819b7 Mon Sep 17 00:00:00 2001 From: Hubert Sosinski Date: Wed, 18 Mar 2026 09:43:43 +0100 Subject: [PATCH 4/4] fix merge conflict --- .../OnyxDerived/configs/reportAttributes.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts index 420555aac821e..00f78d19ef551 100644 --- a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts +++ b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts @@ -92,20 +92,20 @@ export default createOnyxDerivedValueConfig({ // A full recompute is needed when locale changes (report names are locale-dependent) or display names change. // We compare preferredLocale against currentValue?.locale so that the first locale load on startup // (where both equal the same persisted value) does not trigger an unnecessary full recompute. - const needsFullRecompute = (hasKeyTriggeredCompute(ONYXKEYS.NVP_PREFERRED_LOCALE, sourceValues) && preferredLocale !== currentValue?.locale) || displayNamesChanged; - - // Use incremental updates when currentValue is already populated and no full recompute is required. - // If currentValue has no reports (fresh install or cleared storage), fall back to a full scan. - const useIncrementalUpdates = !!currentValue?.reports && Object.keys(currentValue.reports).length > 0 && !needsFullRecompute; + let needsFullRecompute = (hasKeyTriggeredCompute(ONYXKEYS.NVP_PREFERRED_LOCALE, sourceValues) && preferredLocale !== currentValue?.locale) || displayNamesChanged; // if policies are loaded first time, we need to recompute all report attributes to get correct action badge in LHN, such as Approve because it depends on policy's type (see canApproveIOU function) if (hasKeyTriggeredCompute(ONYXKEYS.COLLECTION.POLICY, sourceValues)) { - if (isFullyComputed && Object.keys(previousPolicies ?? {}).length === 0 && Object.keys(policies ?? {}).length > 0) { - isFullyComputed = false; + if (Object.keys(previousPolicies ?? {}).length === 0 && Object.keys(policies ?? {}).length > 0) { + needsFullRecompute = true; } previousPolicies = policies; } + // Use incremental updates when currentValue is already populated and no full recompute is required. + // If currentValue has no reports (fresh install or cleared storage), fall back to a full scan. + const useIncrementalUpdates = !!currentValue?.reports && Object.keys(currentValue.reports).length > 0 && !needsFullRecompute; + // if we already computed the report attributes and there is no new reports data, return the current value if ((useIncrementalUpdates && !sourceValues) || !reports) { return currentValue ?? {reports: {}, locale: null};