Repository navigation
[Due for payment 2026-06-09] [Onyx Audit] Remove initWithStoredValues from useOnyx #80091
Description
Coming from #71202.
In order to simplify useOnyx API (and consequently Onyx) let's work on proposing the removal of some configurations we think are anti-patterns / unnecessary at this point.
Last year @roryabraham drafted this proposal that can be found in this doc, but I will put it below in case you don't have access. We should review the proposal, adjust if necessary and post it.
Original proposal from Rory
Proposal: Introduce RAM-only Onyx keys.
Background
react-native-onyx is a very versatile state management library that is the beating heart of our front-end. It largely relies on a publisher-subscriber (PubSub) pattern to propagate data across various front-end UI components. Furthermore, one of the driving principles of Onyx is supporting a snappy, optimistic, offline-first approach that couples data storage with state management.
Furthermore, we have built systems in our back-end that are tightly coupled with Onyx. Colloquially these systems are often referred to as “reliable updates” or just “Onyx updates”. I like to think of Onyx as a small copy of the back-end database, with just the data that a particular user needs and has access to.
In most cases, and in particular for pieces of data that originate in the back-end, persisting data and then reading it from disk makes sense for our use-cases. However, there are some pieces of data which are ephemeral - short-lived, and not representative of some permanent state. Some examples of this are:
- Is your device offline?
- Are we fetching some data from the back-end?
As a litmus test, if the answer to both of the following questions is yes, then the data is probably ephemeral:
- If I kill the app and open it again, would this data still be accurate?
- Does this data exist only on the client, or is it coming from the server?
We have a way to handle ephemeral data in Onyx - initWithStoredValues. This boolean parameter to useOnyx and Onyx.connectWithoutView defaults to true, but if it’s false, then the first time the data is requested it will not be read from cache or disk immediately, preventing the consumption of stale data stored in Onyx with the same key.
Regardless of whether consumers of a piece of Onyx data pass initWithStoredValues: false, all Onyx data is persisted to disk - that’s just the internal design of Onyx.
Problem
When developers forget (or don’t know) to pass initWithStoredValues: false to useOnyx invocations accessing an ephemeral piece of data, stale data is read from disk, which causes bugs (example).
Solution
Introduce the concept of RAM-only Onyx keys. The way this would work is that Onyx keys can be denoted with the ramonly_ prefix, and then Onyx will not ever write that data to disk, preventing it from ever being read from disk in a stale state. Then deprecate and remove initWithStoredValues from Onyx.
This solves the core problem in that ephemeral data can be configured once in Onyx.init, and all usages of that data will implicitly be treated as such. But it also has some tertiary advantages:
- Simpler: You can’t read stale data from disk if you never write it to disk in the first place.
- More efficient: When we write data to disk with the intention of never reading it from disk, that wastes time and computer resources.
Issue Owner
Current Issue Owner: @Krishna2323I also think it's perfectly valid to do no migration, and just leave the old keys in storage. It's not a big deal.
Metadata
Metadata
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
JKobrynski commentedon Jan 22, 2026
Hi, I'm Julian from Callstack - expert agency - and I'd like to work on this issue
JKobrynski commentedon Jan 22, 2026
Update:
I started working on this issue today. I got familiar with Rory's proposal that he posted some months ago and started exploring how we can introduce it to the current codebase. I'm going to continue my work tomorrow.
fabioh8010 commentedon Jan 23, 2026
Some suggestions to the original proposal:
ramonly_to identify RAM-only keys, we could use a specific entry in ONYXKEYS to group all these keys e.g.ONYXKEYS.RAM_ONLY.<key>just like we have for collection ones (ONYXKEYS.COLLECTION.<key>). In this way I think logic handling inside Onyx will also be easier because it will be cheaper to know if that key is ram-only or not by just checking a Set, instead of performin string operations to check for the prefix.react-native-onyxand mark theinitWithStoredValuesprop as deprecated.initWithStoredValuesinstances in the E/App with the new approach.initWithStoredValuesfrom Onyx.WDYT @JKobrynski @roryabraham ?
JKobrynski commentedon Jan 23, 2026
@fabioh8010
ramonly_prefix in key's name right? E.g.SOME_RAMONLY_KEY: 'ramonly_SOME_KEY'right?fabioh8010 commentedon Jan 23, 2026
@JKobrynski
I left a suggestion in my previous answer. Before it was "I like to think of Onyx as..." and I changed to "We can think of Onyx as...".
If we agree on the grouping I don't think we would need to prefix anymore, would sound redundant to me.
JKobrynski commentedon Jan 23, 2026
Here is the updated version of Rory's proposal (this is still a draft, so it might be changed, I'm happy to address any feedback!):
Proposal:
Introduce RAM-only Onyx keys.
Background
react-native-onyx is a very versatile state management library that is the beating heart of our front-end. It largely relies on a publisher-subscriber (PubSub) pattern to propagate data across various front-end UI components. Furthermore, one of the driving principles of Onyx is supporting a snappy, optimistic, offline-first approach that couples data storage with state management.
Furthermore, we have built systems in our back-end that are tightly coupled with Onyx. Colloquially these systems are often referred to as “reliable updates” or just “Onyx updates”. We can think of Onyx as a small copy of the back-end database, with just the data that a particular user needs and has access to.
In most cases, and in particular for pieces of data that originate in the back-end, persisting data and then reading it from disk makes sense for our use-cases. However, there are some pieces of data which are ephemeral - short-lived, and not representative of some permanent state. Some examples of this are:
As a litmus test, if the answer to both of the following questions is yes, then the data is probably ephemeral:
We have a way to handle ephemeral data in Onyx -
initWithStoredValues. This boolean parameter touseOnyxandOnyx.connectdefaults to true, but if it’s false, then the first time the data is requested it will not be read from cache or disk immediately, preventing the consumption of stale data stored in Onyx with the same key.Regardless of whether consumers of a piece of Onyx data pass
initWithStoredValues: false, all Onyx data is persisted to disk - that’s just the internal design of Onyx.Problem
When developers forget (or don’t know) to pass
initWithStoredValues: falsetouseOnyxinvocations accessing an ephemeral piece of data, stale data is read from disk, which causes bugs (example).Solution
Introduce the concept of RAM-only Onyx keys. The way this would work is that Onyx keys could be grouped, similar to what we do with collections (e.g. like
ONYXKEYS.RAM_ONLY.<key>), and then Onyx will never write that data to disk, preventing it from being read from disk in a stale state. Then deprecate and removeinitWithStoredValuesfrom Onyx.This solves the core problem in that ephemeral data can be configured once in
Onyx.init, and all usages of that data will implicitly be treated as such. But it also has some tertiary advantages:The process would proceed as follows:
initWithStoredValuesproperty as deprecatedinitWithStoredValuesinstances in the E/App with RAM-only Onyx keys.initWithStoredValuesfrom Onyx.CC: @roryabraham
JKobrynski commentedon Jan 23, 2026
Update:
I had a discussion with @fabioh8010 about how we can update the original proposal. I rewrote the proposal with the changes that we agreed on. I posted the draft version of the proposal in a comment above so Rory can share his thoughts about it before it's posted for external review. While I'm waiting for Rory's feedback on the draft, I'm already looking into the
react-native-onyxcodebase to find out how we can implement the suggested changes.224 remaining items
Krishna2323 commentedon Jun 2, 2026
Hey @mountiny @roryabraham, everything seems to be deployed and done here. Requesting a compensation increase for the reviewer role on this issue.
Proposed amount: $1,875
Multiple PRs: Reviewed 10 PRs across two repos (excluding straightforward Onyx version bump PRs):
Expensify/react-native-onyx:
Expensify/App:
Scope expansion: What started as a single Onyx API change grew into a multi-month migration — introducing RAM-only key support in Onyx, migrating all ephemeral keys across the App, handling regressions that required reverts and reapplies, and finally removing
initWithStoredValuesfrom Onyx entirely.Core flow changes: The migration touched core infrastructure (Onyx setup, action files, hooks, authentication flows) requiring thorough cross-platform testing on each PR.
Files touched: The App-side PRs alone modified 100+ files spanning Onyx setup, action files, hooks, and UI components.
Additional fixes: Some migration PRs were reverted due to regressions and had to be reapplied with fixes (e.g., [Onyx audit] Migrate keys to RAM-only - part 1/2 #82309 reverted and reapplied as [Onyx audit] Migrate keys to RAM-only - part 1/2 (including bugfix) #85980, [Onyx Audit] Migrate keys to RAM-only part 2/2 #83450 reverted and reapplied as [Onyx Audit] Migrate keys to RAM-only part 2/3 - fix #87254). I don't think bounties should be deducted for the regressions — the changes touched a really large surface area, and catching every edge case upfront wasn't realistically possible. The reapplied PRs required the same or more review effort.
Breakdown:
That said, I'm completely open to any adjustments the team sees fit. Thank you!
roryabraham commentedon Jun 2, 2026
@Krishna2323 $1875 sounds reasonable to me 👍🏼
melvin-bot commentedon Jun 5, 2026
@Krishna2323 Whoops! This issue is 2 days overdue. Let's get this updated quick!
melvin-bot commentedon Jun 8, 2026
Current assignee @mallenexpensify is eligible for the Awaiting Payment assigner, not assigning anyone new.
Krishna2323 commentedon Jun 8, 2026
@mallenexpensify this is ready for payment, and I don't think a checklist is required here. The payment summary is above ^
melvin-bot commentedon Jun 12, 2026
@Krishna2323 Whoops! This issue is 2 days overdue. Let's get this updated quick!
Krishna2323 commentedon Jun 15, 2026
@mallenexpensify friendly bump :)
mallenexpensify commentedon Jun 15, 2026
Payment Summary
Contributor+: @Krishna2323 due $1875 via NewDot
laurenreidexpensify commentedon Jun 29, 2026
$1875 approved for @Krishna2323