Skip to content

Commit 9c803c8

Browse files
authored
Merge pull request #667 from callstack-internal/feature/remove-withOnyx
Remove withOnyx from the codebase
2 parents a6ead28 + ae45dd0 commit 9c803c8

26 files changed

+115
-2644
lines changed

API-INTERNAL.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,7 @@ If the requested key is a collection, it will return an object with all the coll
8989
<dd><p>When a key change happens, search for any callbacks matching the key or collection key and trigger those callbacks</p>
9090
</dd>
9191
<dt><a href="#sendDataToConnection">sendDataToConnection()</a></dt>
92-
<dd><p>Sends the data obtained from the keys to the connection. It either:
93-
- sets state on the withOnyxInstances
94-
- triggers the callback function</p>
92+
<dd><p>Sends the data obtained from the keys to the connection.</p>
9593
</dd>
9694
<dt><a href="#addKeyToRecentlyAccessedIfNeeded">addKeyToRecentlyAccessedIfNeeded()</a></dt>
9795
<dd><p>We check to see if this key is flagged as safe for eviction and add it to the recentlyAccessedKeys list so that when we
@@ -368,9 +366,7 @@ keyChanged(key, value, subscriber => subscriber.initWithStoredValues === false)
368366
<a name="sendDataToConnection"></a>
369367

370368
## sendDataToConnection()
371-
Sends the data obtained from the keys to the connection. It either:
372-
- sets state on the withOnyxInstances
373-
- triggers the callback function
369+
Sends the data obtained from the keys to the connection.
374370

375371
**Kind**: global function
376372
<a name="addKeyToRecentlyAccessedIfNeeded"></a>

API.md

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,7 @@ This method will be deprecated soon. Please use `Onyx.connectWithoutView()` inst
8181
| connectOptions.key | The Onyx key to subscribe to. |
8282
| connectOptions.callback | A function that will be called when the Onyx data we are subscribed changes. |
8383
| connectOptions.waitForCollectionCallback | If set to `true`, it will return the entire collection to the callback as a single object. |
84-
| connectOptions.withOnyxInstance | The `withOnyx` class instance to be internally passed. **Only used inside `withOnyx()` HOC.** |
85-
| connectOptions.statePropertyName | The name of the component's prop that is connected to the Onyx key. **Only used inside `withOnyx()` HOC.** |
86-
| connectOptions.displayName | The component's display name. **Only used inside `withOnyx()` HOC.** |
87-
| connectOptions.selector | This will be used to subscribe to a subset of an Onyx key's data. **Only used inside `useOnyx()` hook or `withOnyx()` HOC.** Using this setting on `useOnyx()` or `withOnyx()` can have very positive performance benefits because the component will only re-render when the subset of data changes. Otherwise, any change of data on any property would normally cause the component to re-render (and that can be expensive from a performance standpoint). |
84+
| connectOptions.selector | This will be used to subscribe to a subset of an Onyx key's data. **Only used inside `useOnyx()` hook.** Using this setting on `useOnyx()` can have very positive performance benefits because the component will only re-render when the subset of data changes. Otherwise, any change of data on any property would normally cause the component to re-render (and that can be expensive from a performance standpoint). |
8885

8986
**Example**
9087
```ts
@@ -107,10 +104,7 @@ Connects to an Onyx key given the options passed and listens to its changes.
107104
| connectOptions.key | The Onyx key to subscribe to. |
108105
| connectOptions.callback | A function that will be called when the Onyx data we are subscribed changes. |
109106
| connectOptions.waitForCollectionCallback | If set to `true`, it will return the entire collection to the callback as a single object. |
110-
| connectOptions.withOnyxInstance | The `withOnyx` class instance to be internally passed. **Only used inside `withOnyx()` HOC.** |
111-
| connectOptions.statePropertyName | The name of the component's prop that is connected to the Onyx key. **Only used inside `withOnyx()` HOC.** |
112-
| connectOptions.displayName | The component's display name. **Only used inside `withOnyx()` HOC.** |
113-
| connectOptions.selector | This will be used to subscribe to a subset of an Onyx key's data. **Only used inside `useOnyx()` hook or `withOnyx()` HOC.** Using this setting on `useOnyx()` or `withOnyx()` can have very positive performance benefits because the component will only re-render when the subset of data changes. Otherwise, any change of data on any property would normally cause the component to re-render (and that can be expensive from a performance standpoint). |
107+
| connectOptions.selector | This will be used to subscribe to a subset of an Onyx key's data. **Only used inside `useOnyx()` hook.** Using this setting on `useOnyx()` can have very positive performance benefits because the component will only re-render when the subset of data changes. Otherwise, any change of data on any property would normally cause the component to re-render (and that can be expensive from a performance standpoint). |
114108

115109
**Example**
116110
```ts

README.md

Lines changed: 2 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Awesome persistent storage solution wrapped in a Pub/Sub library.
99
- Onyx allows other code to subscribe to changes in data, and then publishes change events whenever data is changed
1010
- Anything needing to read Onyx data needs to:
1111
1. Know what key the data is stored in (for web, you can find this by looking in the JS console > Application > local storage)
12-
2. Subscribe to changes of the data for a particular key or set of keys. React function components use the `useOnyx()` hook (recommended), both class and function components can use `withOnyx()` HOC (deprecated, not-recommended) and non-React libs use `Onyx.connect()`.
12+
2. Subscribe to changes of the data for a particular key or set of keys. React function components use the `useOnyx()` hook and non-React libs use `Onyx.connect()`.
1313
3. Get initialized with the current value of that key from persistent storage (Onyx does this by calling `setState()` or triggering the `callback` with the values currently on disk as part of the connection process)
1414
- Subscribing to Onyx keys is done using a constant defined in `ONYXKEYS`. Each Onyx key represents either a collection of items or a specific entry in storage. For example, since all reports are stored as individual keys like `report_1234`, if code needs to know about all the reports (e.g. display a list of them in the nav menu), then it would subscribe to the key `ONYXKEYS.COLLECTION.REPORT`.
1515

@@ -136,7 +136,7 @@ To teardown the subscription call `Onyx.disconnect()` with the `connectionID` re
136136
Onyx.disconnect(connectionID);
137137
```
138138

139-
We can also access values inside React function components via the `useOnyx()` [hook](https://react.dev/reference/react/hooks) (recommended) or class and function components via the `withOnyx()` [higher order component](https://reactjs.org/docs/higher-order-components.html) (deprecated, not-recommended). When the data changes the component will re-render.
139+
We can also access values inside React function components via the `useOnyx()` [hook](https://react.dev/reference/react/hooks). When the data changes the component will re-render.
140140

141141
```javascript
142142
import React from 'react';
@@ -168,48 +168,6 @@ if (reportsResult.status === 'loading' || sessionResult.status === 'loading') {
168168
// rest of the component's code.
169169
```
170170

171-
> [!warning]
172-
> ## Deprecated Note
173-
> Please note that the `withOnyx()` Higher Order Component (HOC) is now considered deprecated. Use `useOnyx()` hook instead.
174-
175-
```javascript
176-
import React from 'react';
177-
import {withOnyx} from 'react-native-onyx';
178-
179-
const App = ({session}) => (
180-
<View>
181-
{session.token ? <Text>Logged in</Text> : <Text>Logged out</Text> }
182-
</View>
183-
);
184-
185-
export default withOnyx({
186-
session: {
187-
key: ONYXKEYS.SESSION,
188-
},
189-
})(App);
190-
```
191-
192-
Differently from `useOnyx()`, `withOnyx()` will delay the rendering of the wrapped component until all keys/entities have been fetched and passed to the component, this can be convenient for simple cases. This however, can really delay your application if many entities are connected to the same component.
193-
194-
Additionally, if your component has many keys/entities when your component will mount but will receive many updates as data is fetched from DB and passed down to it, as every key that gets fetched will trigger a `setState` on the `withOnyx` HOC. This might cause re-renders on the initial mounting, preventing the component from mounting/rendering in reasonable time, making your app feel slow and even delaying animations.
195-
196-
You can workaround this by passing an additional object with the `shouldDelayUpdates` property set to true. Onyx will then put all the updates in a queue until you decide when then should be applied, the component will receive a function `markReadyForHydration`. A good place to call this function is on the `onLayout` method, which gets triggered after your component has been rendered.
197-
198-
```javascript
199-
const App = ({session, markReadyForHydration}) => (
200-
<View onLayout={() => markReadyForHydration()}>
201-
{session.token ? <Text>Logged in</Text> : <Text>Logged out</Text> }
202-
</View>
203-
);
204-
205-
// Second argument to funciton is `shouldDelayUpdates`
206-
export default withOnyx({
207-
session: {
208-
key: ONYXKEYS.SESSION
209-
},
210-
}, true)(App);
211-
```
212-
213171
### Dependent Onyx Keys and useOnyx()
214172
Some components need to subscribe to multiple Onyx keys at once and sometimes, one key might rely on the data from another key. This is similar to a JOIN in SQL.
215173

@@ -246,24 +204,6 @@ export default App;
246204

247205
* It is VERY important to NOT use empty string default values like `report.policyID || ''`. This results in the key returned to `useOnyx` as `policies_`, which subscribes to the ENTIRE POLICY COLLECTION and is most assuredly not what you were intending. You can use a default of `0` (as long as you are reasonably sure that there is never a policyID=0). This allows Onyx to return `undefined` as the value of the policy key, which is handled by `useOnyx` appropriately.
248206

249-
**Detailed explanation of how this is handled and rendered with `withOnyx` HOC:**
250-
1. The component mounts with a `reportID={1234}` prop
251-
2. `withOnyx` evaluates the mapping
252-
3. `withOnyx` connects to the key `reports_1234` because of the prop passed to the component
253-
3. `withOnyx` connects to the key `policies_undefined` because `report` doesn't exist in the props yet, so the `policyID` defaults to `undefined`. * (see note below)
254-
4. Onyx reads the data and updates the state of `withOnyx` with:
255-
- `report={{reportID: 1234, policyID: 1, ... the rest of the object ...}}`
256-
- `policy={undefined}` (since there is no policy with ID `undefined`)
257-
5. There is still an `undefined` key in the mapping, so Onyx reads the data again
258-
6. This time `withOnyx` connects to the key `policies_1` because the `report` object exists in the component's state and it has a `policyID: 1`
259-
7. Onyx reads the data and updates the state of withOnyx with:
260-
- `policy={{policyID: 1, ... the rest of the object ...}`
261-
8. Now all mappings have values that are defined (not undefined) and the component is rendered with all necessary data
262-
263-
* It is VERY important to NOT use empty string default values like `report.policyID || ''`. This results in the key returned to `withOnyx` as `policies_` which subscribes to the ENTIRE POLICY COLLECTION and is most assuredly not what you were intending. You can use a default of `0` (as long as you are reasonably sure that there is never a policyID=0). This allows Onyx to return `undefined` as the value of the policy key, which is handled by `withOnyx` appropriately.
264-
265-
DO NOT use more than one `withOnyx` component at a time. It adds overhead and prevents some optimizations like batched rendering from working to its full potential.
266-
267207
It's also beneficial to use a [selector](https://github.com/Expensify/react-native-onyx/blob/main/API.md#connectmapping--number) with the mapping in case you need to grab a single item in a collection (like a single report action).
268208

269209
### useOnyx()'s `canBeMissing` option

lib/Onyx.ts

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as Logger from './Logger';
22
import cache, {TASK} from './OnyxCache';
3-
import * as PerformanceUtils from './PerformanceUtils';
43
import Storage from './storage';
54
import utils from './utils';
65
import DevTools from './DevTools';
@@ -11,7 +10,6 @@ import type {
1110
ConnectOptions,
1211
InitOptions,
1312
KeyValueMapping,
14-
Mapping,
1513
OnyxInputKeyValueMapping,
1614
OnyxCollection,
1715
MixedOperationsQueue,
@@ -41,7 +39,6 @@ function init({
4139
evictableKeys = [],
4240
maxCachedKeysCount = 1000,
4341
shouldSyncMultipleInstances = !!global.localStorage,
44-
debugSetState = false,
4542
enablePerformanceMetrics = false,
4643
skippableCollectionMemberIDs = [],
4744
}: InitOptions): void {
@@ -56,16 +53,11 @@ function init({
5653

5754
if (shouldSyncMultipleInstances) {
5855
Storage.keepInstancesSync?.((key, value) => {
59-
const prevValue = cache.get(key, false) as OnyxValue<typeof key>;
6056
cache.set(key, value);
61-
OnyxUtils.keyChanged(key, value as OnyxValue<typeof key>, prevValue);
57+
OnyxUtils.keyChanged(key, value as OnyxValue<typeof key>);
6258
});
6359
}
6460

65-
if (debugSetState) {
66-
PerformanceUtils.setShouldDebugSetState(true);
67-
}
68-
6961
if (maxCachedKeysCount > 0) {
7062
cache.setRecentKeysLimit(maxCachedKeysCount);
7163
}
@@ -94,11 +86,8 @@ function init({
9486
* @param connectOptions.key The Onyx key to subscribe to.
9587
* @param connectOptions.callback A function that will be called when the Onyx data we are subscribed changes.
9688
* @param connectOptions.waitForCollectionCallback If set to `true`, it will return the entire collection to the callback as a single object.
97-
* @param connectOptions.withOnyxInstance The `withOnyx` class instance to be internally passed. **Only used inside `withOnyx()` HOC.**
98-
* @param connectOptions.statePropertyName The name of the component's prop that is connected to the Onyx key. **Only used inside `withOnyx()` HOC.**
99-
* @param connectOptions.displayName The component's display name. **Only used inside `withOnyx()` HOC.**
100-
* @param connectOptions.selector This will be used to subscribe to a subset of an Onyx key's data. **Only used inside `useOnyx()` hook or `withOnyx()` HOC.**
101-
* Using this setting on `useOnyx()` or `withOnyx()` can have very positive performance benefits because the component will only re-render
89+
* @param connectOptions.selector This will be used to subscribe to a subset of an Onyx key's data. **Only used inside `useOnyx()` hook.**
90+
* Using this setting on `useOnyx()` can have very positive performance benefits because the component will only re-render
10291
* when the subset of data changes. Otherwise, any change of data on any property would normally
10392
* cause the component to re-render (and that can be expensive from a performance standpoint).
10493
* @returns The connection object to use when calling `Onyx.disconnect()`.
@@ -122,11 +111,8 @@ function connect<TKey extends OnyxKey>(connectOptions: ConnectOptions<TKey>): Co
122111
* @param connectOptions.key The Onyx key to subscribe to.
123112
* @param connectOptions.callback A function that will be called when the Onyx data we are subscribed changes.
124113
* @param connectOptions.waitForCollectionCallback If set to `true`, it will return the entire collection to the callback as a single object.
125-
* @param connectOptions.withOnyxInstance The `withOnyx` class instance to be internally passed. **Only used inside `withOnyx()` HOC.**
126-
* @param connectOptions.statePropertyName The name of the component's prop that is connected to the Onyx key. **Only used inside `withOnyx()` HOC.**
127-
* @param connectOptions.displayName The component's display name. **Only used inside `withOnyx()` HOC.**
128-
* @param connectOptions.selector This will be used to subscribe to a subset of an Onyx key's data. **Only used inside `useOnyx()` hook or `withOnyx()` HOC.**
129-
* Using this setting on `useOnyx()` or `withOnyx()` can have very positive performance benefits because the component will only re-render
114+
* @param connectOptions.selector This will be used to subscribe to a subset of an Onyx key's data. **Only used inside `useOnyx()` hook.**
115+
* Using this setting on `useOnyx()` can have very positive performance benefits because the component will only re-render
130116
* when the subset of data changes. Otherwise, any change of data on any property would normally
131117
* cause the component to re-render (and that can be expensive from a performance standpoint).
132118
* @returns The connection object to use when calling `Onyx.disconnect()`.
@@ -260,7 +246,6 @@ function multiSet(data: OnyxMultiSetInput): Promise<void> {
260246
const keyValuePairsToSet = OnyxUtils.prepareKeyValuePairsForStorage(newData, true);
261247

262248
const updatePromises = keyValuePairsToSet.map(([key, value]) => {
263-
const prevValue = cache.get(key, false);
264249
// When we use multiSet to set a key we want to clear the current delta changes from Onyx.merge that were queued
265250
// before the value was set. If Onyx.merge is currently reading the old value from storage, it will then not apply the changes.
266251
if (OnyxUtils.hasPendingMergeForKey(key)) {
@@ -269,7 +254,7 @@ function multiSet(data: OnyxMultiSetInput): Promise<void> {
269254

270255
// Update cache and optimistically inform subscribers on the next tick
271256
cache.set(key, value);
272-
return OnyxUtils.scheduleSubscriberUpdate(key, value, prevValue);
257+
return OnyxUtils.scheduleSubscriberUpdate(key, value);
273258
});
274259

275260
return Storage.multiSet(keyValuePairsToSet)
@@ -476,7 +461,7 @@ function clear(keysToPreserve: OnyxKey[] = []): Promise<void> {
476461

477462
// Notify the subscribers for each key/value group so they can receive the new values
478463
Object.entries(keyValuesToResetIndividually).forEach(([key, value]) => {
479-
updatePromises.push(OnyxUtils.scheduleSubscriberUpdate(key, value, cache.get(key, false)));
464+
updatePromises.push(OnyxUtils.scheduleSubscriberUpdate(key, value));
480465
});
481466
Object.entries(keyValuesToResetAsCollection).forEach(([key, value]) => {
482467
updatePromises.push(OnyxUtils.scheduleNotifyCollectionSubscribers(key, value));
@@ -764,4 +749,4 @@ function applyDecorators() {
764749
}
765750

766751
export default Onyx;
767-
export type {OnyxUpdate, Mapping, ConnectOptions, SetOptions};
752+
export type {OnyxUpdate, ConnectOptions, SetOptions};

0 commit comments

Comments
 (0)