From bc5c2da61d36f6d8e3917fe696e075764682847d Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 3 Apr 2025 15:40:39 +0200 Subject: [PATCH 1/4] add guidelines to readme --- README.md | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/README.md b/README.md index 21df98a76a60..8d566bb11739 100644 --- a/README.md +++ b/README.md @@ -854,6 +854,47 @@ To build an APK to share run (e.g. via Slack), run `npm run android-build`, this # Onyx derived values Onyx derived values are special Onyx keys which contain values derived from other Onyx values. These are available as a performance optimization, so that if the result of a common computation of Onyx values is needed in many places across the app, the computation can be done only as needed in a centralized location, and then shared across the app. Once created, Onyx derived values are stored and consumed just like any other Onyx value. +## When to use derived values? + +1. **Complex Computations Across Multiple Components** + - Multiple components need the same computed value from one or more Onyx keys + - The computation is expensive (e.g., filtering large arrays, complex object transformations) + - The result needs to be cached and shared to avoid redundant calculations + +2. **Performance Critical Paths** + - The computation appears in frequently rendered components + - Profiling shows the same calculation being done repeatedly + - The computation involves multiple Onyx dependencies that change independently + +3. **Data Aggregation and Transformation** + - You need to combine data from multiple Onyx keys into a single, normalized structure + - The transformation logic is complex and reusable + - The derived data structure is used in multiple places + +4. **State-Dependent Calculations** + - The value depends on multiple pieces of state that can change independently + - The relationship between states is complex (e.g., filtering + sorting + grouping) + - Changes in any dependency should trigger a recalculation + +## When not to use derived values? + +1. **Simple or Local Computations** + - The computation is trivial (e.g., simple string manipulation, basic math) + - The value is only used in one component + +2. **High-Frequency Updates** + - The dependencies change very frequently + - The derived value would trigger too many re-renders + +3. **Component-Specific Logic** + - The computation is specific to a single component's UI state + - The logic involves component-local state + +4. **Temporary or Volatile Data** + - The computed value is only needed temporarily + - The data doesn't need to persist across component unmounts + - The computation depends on non-Onyx values + ## Creating new Onyx derived values 1. Add the new Onyx key. The keys for Onyx derived values are stored in `ONYXKEYS.ts`, in the `ONYXKEYS.DERIVED` object. 2. Declare the type for the derived value in `ONYXKEYS.ts`, in the `OnyxDerivedValuesMapping` type. @@ -861,3 +902,45 @@ Onyx derived values are special Onyx keys which contain values derived from othe 1. The Onyx key for the derived value 2. An array of dependent Onyx keys (which can be any keys, not including the one from the previous step. Including other derived values!) 3. A `compute` function, which takes an array of dependent Onyx values (in the same order as the array of keys from the previous step), and returns a value matching the type you declared in `OnyxDerivedValuesMapping` + +## Best practices + +1. **Keep computations pure and predictable** + ```typescript + // GOOD ✅ + compute: ([reports, personalDetails]) => { + // Pure function, only depends on input + return reports.map(report => ({ + ...report, + authorName: personalDetails[report.authorID]?.displayName + })); + } + + // BAD ❌ + compute: ([reports]) => { + // Don't use external state or cause side effects + const currentUser = getCurrentUser(); // External dependency! + sendAnalytics('computation-done'); // Side effect! + return reports; + } + ``` +2. **Handle edge cases** + ```typescript + // GOOD ✅ + compute: ([reports, personalDetails]: [Report[], PersonalDetails]): DerivedType => { + if (!reports?.length || !personalDetails) { + return { items: [], count: 0 }; + } + // Rest of computation... + } + + // BAD ❌ + compute: ([reports, personalDetails]) => { + // Missing type safety and edge cases + return reports.map(report => personalDetails[report.id]); + } + ``` + +3. **Document derived values** + - Explain the purpose and dependencies + - Document any special cases or performance considerations \ No newline at end of file From 0ce3fd7f6acf07d16032ce58f5e4fc27e07b33b1 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 4 Apr 2025 12:19:58 +0200 Subject: [PATCH 2/4] remove re-renders point --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 8d566bb11739..2c7cee7a110a 100644 --- a/README.md +++ b/README.md @@ -884,7 +884,6 @@ Onyx derived values are special Onyx keys which contain values derived from othe 2. **High-Frequency Updates** - The dependencies change very frequently - - The derived value would trigger too many re-renders 3. **Component-Specific Logic** - The computation is specific to a single component's UI state From 13430a958edb4cdd59c8d701fa531f5412e99dcf Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 7 Apr 2025 08:51:05 +0200 Subject: [PATCH 3/4] update guidelines --- Mobile-Expensify | 2 +- README.md | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index a15fcfc9292e..a3e374271b4f 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit a15fcfc9292e2b9ea0ef203fbb99516553aa67fc +Subproject commit a3e374271b4ffea3e038075ffe64e341b0772bc2 diff --git a/README.md b/README.md index 2c7cee7a110a..abd25c99ef48 100644 --- a/README.md +++ b/README.md @@ -882,14 +882,11 @@ Onyx derived values are special Onyx keys which contain values derived from othe - The computation is trivial (e.g., simple string manipulation, basic math) - The value is only used in one component -2. **High-Frequency Updates** - - The dependencies change very frequently - -3. **Component-Specific Logic** +2. **Component-Specific Logic** - The computation is specific to a single component's UI state - The logic involves component-local state -4. **Temporary or Volatile Data** +3. **Temporary or Volatile Data** - The computed value is only needed temporarily - The data doesn't need to persist across component unmounts - The computation depends on non-Onyx values From 6f7b4b6d88edc758f68f7279a1150d00f72ea201 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 9 Apr 2025 08:25:45 +0200 Subject: [PATCH 4/4] update submodule --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index a3e374271b4f..40cc5e90dc58 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit a3e374271b4ffea3e038075ffe64e341b0772bc2 +Subproject commit 40cc5e90dc584c2a36fe05fb8828c723843a988e