react-native-onyx 2.0.17 → 2.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +34 -27
- package/dist/Onyx.d.ts +36 -3
- package/dist/Onyx.js +15 -1
- package/dist/index.d.ts +19 -2
- package/dist/index.js +3 -1
- package/dist/storage/__mocks__/index.js +6 -2
- package/dist/types.d.ts +6 -0
- package/dist/useLiveRef.d.ts +7 -0
- package/dist/useLiveRef.js +13 -0
- package/dist/useOnyx.d.ts +36 -0
- package/dist/useOnyx.js +121 -0
- package/dist/usePrevious.d.ts +5 -0
- package/dist/usePrevious.js +14 -0
- package/package.json +4 -4
package/API.md
CHANGED
|
@@ -22,8 +22,15 @@ cause react to schedule the updates at once instead of after each other. This is
|
|
|
22
22
|
and runs it through a reducer function to return a subset of the data according to a selector.
|
|
23
23
|
The resulting collection will only contain items that are returned by the selector.</p>
|
|
24
24
|
</dd>
|
|
25
|
+
<dt><a href="#isCollectionKey">isCollectionKey(key)</a> ⇒ <code>Boolean</code></dt>
|
|
26
|
+
<dd><p>Checks to see if the a subscriber's supplied key
|
|
27
|
+
is associated with a collection of keys.</p>
|
|
28
|
+
</dd>
|
|
25
29
|
<dt><a href="#isCollectionMemberKey">isCollectionMemberKey(collectionKey, key)</a> ⇒ <code>Boolean</code></dt>
|
|
26
30
|
<dd></dd>
|
|
31
|
+
<dt><a href="#splitCollectionMemberKey">splitCollectionMemberKey(key)</a> ⇒ <code>Array.<String></code></dt>
|
|
32
|
+
<dd><p>Splits a collection member key into the collection key part and the ID part.</p>
|
|
33
|
+
</dd>
|
|
27
34
|
<dt><a href="#tryGetCachedValue">tryGetCachedValue(key, mapping)</a> ⇒ <code>Mixed</code></dt>
|
|
28
35
|
<dd><p>Tries to get a value from the cache. If the value is not present in cache it will return the default value or undefined.
|
|
29
36
|
If the requested key is a collection, it will return an object with all the collection members.</p>
|
|
@@ -34,7 +41,7 @@ If the requested key is a collection, it will return an object with all the coll
|
|
|
34
41
|
<dt><a href="#disconnect">disconnect(connectionID, [keyToRemoveFromEvictionBlocklist])</a></dt>
|
|
35
42
|
<dd><p>Remove the listener for a react component</p>
|
|
36
43
|
</dd>
|
|
37
|
-
<dt><a href="#scheduleSubscriberUpdate">scheduleSubscriberUpdate(key, value, [canUpdateSubscriber])</a> ⇒ <code>Promise</code></dt>
|
|
44
|
+
<dt><a href="#scheduleSubscriberUpdate">scheduleSubscriberUpdate(key, value, prevValue, [canUpdateSubscriber])</a> ⇒ <code>Promise</code></dt>
|
|
38
45
|
<dd><p>Schedules an update that will be appended to the macro task queue (so it doesn't update the subscribers immediately).</p>
|
|
39
46
|
</dd>
|
|
40
47
|
<dt><a href="#scheduleNotifyCollectionSubscribers">scheduleNotifyCollectionSubscribers(key, value)</a> ⇒ <code>Promise</code></dt>
|
|
@@ -90,13 +97,6 @@ value will be saved to storage after the default value.</p>
|
|
|
90
97
|
<dt><a href="#setMemoryOnlyKeys">setMemoryOnlyKeys(keyList)</a></dt>
|
|
91
98
|
<dd><p>When set these keys will not be persisted to storage</p>
|
|
92
99
|
</dd>
|
|
93
|
-
<dt><a href="#onClear">onClear(callback)</a></dt>
|
|
94
|
-
<dd><p>Sets the callback to be called when the clear finishes executing.</p>
|
|
95
|
-
</dd>
|
|
96
|
-
<dt><a href="#subscribeToEvents">subscribeToEvents()</a></dt>
|
|
97
|
-
<dd><p>Subscribes to the Broadcast channel and executes actions based on the
|
|
98
|
-
types of events.</p>
|
|
99
|
-
</dd>
|
|
100
100
|
<dt><a href="#init">init([options])</a></dt>
|
|
101
101
|
<dd><p>Initialize the store with actions and listening for storage events</p>
|
|
102
102
|
</dd>
|
|
@@ -153,6 +153,18 @@ The resulting collection will only contain items that are returned by the select
|
|
|
153
153
|
| selector | <code>String</code> \| <code>function</code> | (see method docs for getSubsetOfData() for full details) |
|
|
154
154
|
| [withOnyxInstanceState] | <code>Object</code> | |
|
|
155
155
|
|
|
156
|
+
<a name="isCollectionKey"></a>
|
|
157
|
+
|
|
158
|
+
## isCollectionKey(key) ⇒ <code>Boolean</code>
|
|
159
|
+
Checks to see if the a subscriber's supplied key
|
|
160
|
+
is associated with a collection of keys.
|
|
161
|
+
|
|
162
|
+
**Kind**: global function
|
|
163
|
+
|
|
164
|
+
| Param | Type |
|
|
165
|
+
| --- | --- |
|
|
166
|
+
| key | <code>String</code> |
|
|
167
|
+
|
|
156
168
|
<a name="isCollectionMemberKey"></a>
|
|
157
169
|
|
|
158
170
|
## isCollectionMemberKey(collectionKey, key) ⇒ <code>Boolean</code>
|
|
@@ -163,6 +175,18 @@ The resulting collection will only contain items that are returned by the select
|
|
|
163
175
|
| collectionKey | <code>String</code> |
|
|
164
176
|
| key | <code>String</code> |
|
|
165
177
|
|
|
178
|
+
<a name="splitCollectionMemberKey"></a>
|
|
179
|
+
|
|
180
|
+
## splitCollectionMemberKey(key) ⇒ <code>Array.<String></code>
|
|
181
|
+
Splits a collection member key into the collection key part and the ID part.
|
|
182
|
+
|
|
183
|
+
**Kind**: global function
|
|
184
|
+
**Returns**: <code>Array.<String></code> - A tuple where the first element is the collection part and the second element is the ID part.
|
|
185
|
+
|
|
186
|
+
| Param | Type | Description |
|
|
187
|
+
| --- | --- | --- |
|
|
188
|
+
| key | <code>String</code> | The collection member key to split. |
|
|
189
|
+
|
|
166
190
|
<a name="tryGetCachedValue"></a>
|
|
167
191
|
|
|
168
192
|
## tryGetCachedValue(key, mapping) ⇒ <code>Mixed</code>
|
|
@@ -221,7 +245,7 @@ Onyx.disconnect(connectionID);
|
|
|
221
245
|
```
|
|
222
246
|
<a name="scheduleSubscriberUpdate"></a>
|
|
223
247
|
|
|
224
|
-
## scheduleSubscriberUpdate(key, value, [canUpdateSubscriber]) ⇒ <code>Promise</code>
|
|
248
|
+
## scheduleSubscriberUpdate(key, value, prevValue, [canUpdateSubscriber]) ⇒ <code>Promise</code>
|
|
225
249
|
Schedules an update that will be appended to the macro task queue (so it doesn't update the subscribers immediately).
|
|
226
250
|
|
|
227
251
|
**Kind**: global function
|
|
@@ -230,6 +254,7 @@ Schedules an update that will be appended to the macro task queue (so it doesn't
|
|
|
230
254
|
| --- | --- | --- |
|
|
231
255
|
| key | <code>String</code> | |
|
|
232
256
|
| value | <code>\*</code> | |
|
|
257
|
+
| prevValue | <code>\*</code> | |
|
|
233
258
|
| [canUpdateSubscriber] | <code>function</code> | only subscribers that pass this truth test will be updated |
|
|
234
259
|
|
|
235
260
|
**Example**
|
|
@@ -410,24 +435,6 @@ When set these keys will not be persisted to storage
|
|
|
410
435
|
| --- | --- |
|
|
411
436
|
| keyList | <code>Array.<string></code> |
|
|
412
437
|
|
|
413
|
-
<a name="onClear"></a>
|
|
414
|
-
|
|
415
|
-
## onClear(callback)
|
|
416
|
-
Sets the callback to be called when the clear finishes executing.
|
|
417
|
-
|
|
418
|
-
**Kind**: global function
|
|
419
|
-
|
|
420
|
-
| Param | Type |
|
|
421
|
-
| --- | --- |
|
|
422
|
-
| callback | <code>function</code> |
|
|
423
|
-
|
|
424
|
-
<a name="subscribeToEvents"></a>
|
|
425
|
-
|
|
426
|
-
## subscribeToEvents()
|
|
427
|
-
Subscribes to the Broadcast channel and executes actions based on the
|
|
428
|
-
types of events.
|
|
429
|
-
|
|
430
|
-
**Kind**: global function
|
|
431
438
|
<a name="init"></a>
|
|
432
439
|
|
|
433
440
|
## init([options])
|
package/dist/Onyx.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {Component} from 'react';
|
|
2
2
|
import * as Logger from './Logger';
|
|
3
|
-
import {CollectionKey, CollectionKeyBase, DeepRecord, KeyValueMapping, NullishDeep, OnyxCollection, OnyxEntry, OnyxKey} from './types';
|
|
3
|
+
import {CollectionKey, CollectionKeyBase, DeepRecord, KeyValueMapping, NullishDeep, OnyxCollection, OnyxEntry, OnyxKey, Selector} from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Represents a mapping object where each `OnyxKey` maps to either a value of its corresponding type in `KeyValueMapping` or `null`.
|
|
@@ -20,6 +20,11 @@ type BaseConnectOptions = {
|
|
|
20
20
|
initWithStoredValues?: boolean;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
type TryGetCachedValueMapping<TKey extends OnyxKey> = {
|
|
24
|
+
selector?: Selector<TKey, unknown, unknown>;
|
|
25
|
+
withOnyxInstance?: Component;
|
|
26
|
+
};
|
|
27
|
+
|
|
23
28
|
/**
|
|
24
29
|
* Represents the options used in `Onyx.connect()` method.
|
|
25
30
|
* The type is built from `BaseConnectOptions` and extended to handle key/callback related options.
|
|
@@ -35,7 +40,7 @@ type BaseConnectOptions = {
|
|
|
35
40
|
type ConnectOptions<TKey extends OnyxKey> = BaseConnectOptions &
|
|
36
41
|
(
|
|
37
42
|
| {
|
|
38
|
-
key: TKey extends
|
|
43
|
+
key: TKey extends CollectionKeyBase ? TKey : never;
|
|
39
44
|
callback?: (value: OnyxCollection<KeyValueMapping[TKey]>) => void;
|
|
40
45
|
waitForCollectionCallback: true;
|
|
41
46
|
}
|
|
@@ -114,6 +119,21 @@ declare const METHOD: {
|
|
|
114
119
|
*/
|
|
115
120
|
declare function getAllKeys(): Promise<Array<OnyxKey>>;
|
|
116
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Checks to see if the a subscriber's supplied key
|
|
124
|
+
* is associated with a collection of keys.
|
|
125
|
+
*/
|
|
126
|
+
declare function isCollectionKey(key: OnyxKey): key is CollectionKeyBase;
|
|
127
|
+
|
|
128
|
+
declare function isCollectionMemberKey<TCollectionKey extends CollectionKeyBase>(collectionKey: TCollectionKey, key: string): key is `${TCollectionKey}${string}`;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Splits a collection member key into the collection key part and the ID part.
|
|
132
|
+
* @param key - The collection member key to split.
|
|
133
|
+
* @returns A tuple where the first element is the collection part and the second element is the ID part.
|
|
134
|
+
*/
|
|
135
|
+
declare function splitCollectionMemberKey<TKey extends CollectionKey>(key: TKey): [TKey extends `${infer Prefix}_${string}` ? `${Prefix}_` : never, string];
|
|
136
|
+
|
|
117
137
|
/**
|
|
118
138
|
* Checks to see if this key has been flagged as
|
|
119
139
|
* safe for removal.
|
|
@@ -289,6 +309,15 @@ declare function hasPendingMergeForKey(key: OnyxKey): boolean;
|
|
|
289
309
|
*/
|
|
290
310
|
declare function setMemoryOnlyKeys(keyList: OnyxKey[]): void;
|
|
291
311
|
|
|
312
|
+
/**
|
|
313
|
+
* Tries to get a value from the cache. If the value is not present in cache it will return the default value or undefined.
|
|
314
|
+
* If the requested key is a collection, it will return an object with all the collection members.
|
|
315
|
+
*/
|
|
316
|
+
declare function tryGetCachedValue<TKey extends OnyxKey>(
|
|
317
|
+
key: TKey,
|
|
318
|
+
mapping?: TryGetCachedValueMapping,
|
|
319
|
+
): TKey extends CollectionKeyBase ? OnyxCollection<KeyValueMapping[TKey]> | undefined : OnyxEntry<KeyValueMapping[TKey]> | undefined;
|
|
320
|
+
|
|
292
321
|
declare const Onyx: {
|
|
293
322
|
connect: typeof connect;
|
|
294
323
|
disconnect: typeof disconnect;
|
|
@@ -307,7 +336,11 @@ declare const Onyx: {
|
|
|
307
336
|
isSafeEvictionKey: typeof isSafeEvictionKey;
|
|
308
337
|
METHOD: typeof METHOD;
|
|
309
338
|
setMemoryOnlyKeys: typeof setMemoryOnlyKeys;
|
|
339
|
+
tryGetCachedValue: typeof tryGetCachedValue;
|
|
340
|
+
isCollectionKey: typeof isCollectionKey;
|
|
341
|
+
isCollectionMemberKey: typeof isCollectionMemberKey;
|
|
342
|
+
splitCollectionMemberKey: typeof splitCollectionMemberKey;
|
|
310
343
|
};
|
|
311
344
|
|
|
312
345
|
export default Onyx;
|
|
313
|
-
export {
|
|
346
|
+
export {ConnectOptions, OnyxUpdate};
|
package/dist/Onyx.js
CHANGED
|
@@ -189,7 +189,6 @@ function getAllKeys() {
|
|
|
189
189
|
* Checks to see if the a subscriber's supplied key
|
|
190
190
|
* is associated with a collection of keys.
|
|
191
191
|
*
|
|
192
|
-
* @private
|
|
193
192
|
* @param {String} key
|
|
194
193
|
* @returns {Boolean}
|
|
195
194
|
*/
|
|
@@ -204,6 +203,18 @@ function isCollectionKey(key) {
|
|
|
204
203
|
function isCollectionMemberKey(collectionKey, key) {
|
|
205
204
|
return Str.startsWith(key, collectionKey) && key.length > collectionKey.length;
|
|
206
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Splits a collection member key into the collection key part and the ID part.
|
|
208
|
+
* @param {String} key - The collection member key to split.
|
|
209
|
+
* @returns {Array<String>} A tuple where the first element is the collection part and the second element is the ID part.
|
|
210
|
+
*/
|
|
211
|
+
function splitCollectionMemberKey(key) {
|
|
212
|
+
const underscoreIndex = key.indexOf('_');
|
|
213
|
+
if (underscoreIndex === -1) {
|
|
214
|
+
throw new Error(`Invalid ${key} key provided, only collection keys are allowed.`);
|
|
215
|
+
}
|
|
216
|
+
return [key.substring(0, underscoreIndex + 1), key.substring(underscoreIndex + 1)];
|
|
217
|
+
}
|
|
207
218
|
/**
|
|
208
219
|
* Checks to see if a provided key is the exact configured key of our connected subscriber
|
|
209
220
|
* or if the provided key is a collection member key (in case our configured key is a "collection key")
|
|
@@ -1483,5 +1494,8 @@ const Onyx = {
|
|
|
1483
1494
|
setMemoryOnlyKeys,
|
|
1484
1495
|
tryGetCachedValue,
|
|
1485
1496
|
hasPendingMergeForKey,
|
|
1497
|
+
isCollectionKey,
|
|
1498
|
+
isCollectionMemberKey,
|
|
1499
|
+
splitCollectionMemberKey,
|
|
1486
1500
|
};
|
|
1487
1501
|
exports.default = Onyx;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import Onyx, {OnyxUpdate, ConnectOptions} from './Onyx';
|
|
2
|
-
import {CustomTypeOptions, OnyxCollection, OnyxEntry, NullishDeep, KeyValueMapping, OnyxKey, Selector, WithOnyxInstanceState} from './types';
|
|
2
|
+
import {CustomTypeOptions, OnyxCollection, OnyxEntry, NullishDeep, KeyValueMapping, OnyxKey, Selector, WithOnyxInstanceState, OnyxValue} from './types';
|
|
3
3
|
import withOnyx from './withOnyx';
|
|
4
|
+
import useOnyx, {UseOnyxResult, FetchStatus} from './useOnyx';
|
|
4
5
|
|
|
5
6
|
export default Onyx;
|
|
6
|
-
export {
|
|
7
|
+
export {
|
|
8
|
+
CustomTypeOptions,
|
|
9
|
+
OnyxCollection,
|
|
10
|
+
OnyxEntry,
|
|
11
|
+
OnyxUpdate,
|
|
12
|
+
withOnyx,
|
|
13
|
+
ConnectOptions,
|
|
14
|
+
NullishDeep,
|
|
15
|
+
KeyValueMapping,
|
|
16
|
+
OnyxKey,
|
|
17
|
+
Selector,
|
|
18
|
+
WithOnyxInstanceState,
|
|
19
|
+
useOnyx,
|
|
20
|
+
UseOnyxResult,
|
|
21
|
+
OnyxValue,
|
|
22
|
+
FetchStatus,
|
|
23
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -3,8 +3,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.withOnyx = void 0;
|
|
6
|
+
exports.useOnyx = exports.withOnyx = void 0;
|
|
7
7
|
const Onyx_1 = __importDefault(require("./Onyx"));
|
|
8
8
|
const withOnyx_1 = __importDefault(require("./withOnyx"));
|
|
9
9
|
exports.withOnyx = withOnyx_1.default;
|
|
10
|
+
const useOnyx_1 = __importDefault(require("./useOnyx"));
|
|
11
|
+
exports.useOnyx = useOnyx_1.default;
|
|
10
12
|
exports.default = Onyx_1.default;
|
|
@@ -15,13 +15,17 @@ const idbKeyvalMock = {
|
|
|
15
15
|
},
|
|
16
16
|
multiSet(pairs) {
|
|
17
17
|
const setPromises = pairs.map(([key, value]) => this.setItem(key, value));
|
|
18
|
-
return new Promise((resolve) =>
|
|
18
|
+
return new Promise((resolve) => {
|
|
19
|
+
Promise.all(setPromises).then(() => resolve(storageMapInternal));
|
|
20
|
+
});
|
|
19
21
|
},
|
|
20
22
|
getItem(key) {
|
|
21
23
|
return Promise.resolve(storageMapInternal[key]);
|
|
22
24
|
},
|
|
23
25
|
multiGet(keys) {
|
|
24
|
-
const getPromises = keys.map((key) => new Promise((resolve) =>
|
|
26
|
+
const getPromises = keys.map((key) => new Promise((resolve) => {
|
|
27
|
+
this.getItem(key).then((value) => resolve([key, value]));
|
|
28
|
+
}));
|
|
25
29
|
return Promise.all(getPromises);
|
|
26
30
|
},
|
|
27
31
|
multiMerge(pairs) {
|
package/dist/types.d.ts
CHANGED
|
@@ -99,6 +99,11 @@ type CollectionKey = `${CollectionKeyBase}${string}`;
|
|
|
99
99
|
*/
|
|
100
100
|
type OnyxKey = Key | CollectionKey;
|
|
101
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Represents a Onyx value that can be either a single entry or a collection of entries, depending on the `TKey` provided.
|
|
104
|
+
*/
|
|
105
|
+
type OnyxValue<TKey extends OnyxKey> = TKey extends CollectionKeyBase ? OnyxCollection<KeyValueMapping[TKey]> : OnyxEntry<KeyValueMapping[TKey]>;
|
|
106
|
+
|
|
102
107
|
/**
|
|
103
108
|
* Represents a mapping of Onyx keys to values, where keys are either normal or collection Onyx keys
|
|
104
109
|
* and values are the corresponding values in Onyx's state.
|
|
@@ -239,6 +244,7 @@ export {
|
|
|
239
244
|
OnyxCollection,
|
|
240
245
|
OnyxEntry,
|
|
241
246
|
OnyxKey,
|
|
247
|
+
OnyxValue,
|
|
242
248
|
Selector,
|
|
243
249
|
NullishDeep,
|
|
244
250
|
WithOnyxInstanceState,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
/**
|
|
3
|
+
* Creates a mutable reference to a value, useful when you need to
|
|
4
|
+
* maintain a reference to a value that may change over time without triggering re-renders.
|
|
5
|
+
*/
|
|
6
|
+
declare function useLiveRef<T>(value: T): import("react").MutableRefObject<T>;
|
|
7
|
+
export default useLiveRef;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const react_1 = require("react");
|
|
4
|
+
/**
|
|
5
|
+
* Creates a mutable reference to a value, useful when you need to
|
|
6
|
+
* maintain a reference to a value that may change over time without triggering re-renders.
|
|
7
|
+
*/
|
|
8
|
+
function useLiveRef(value) {
|
|
9
|
+
const ref = (0, react_1.useRef)(value);
|
|
10
|
+
ref.current = value;
|
|
11
|
+
return ref;
|
|
12
|
+
}
|
|
13
|
+
exports.default = useLiveRef;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { IsEqual } from 'type-fest';
|
|
2
|
+
import type { CollectionKeyBase, OnyxCollection, OnyxKey, OnyxValue, Selector } from './types';
|
|
3
|
+
type UseOnyxOptions<TKey extends OnyxKey, TReturnValue> = {
|
|
4
|
+
/**
|
|
5
|
+
* Determines if this key in this subscription is safe to be evicted.
|
|
6
|
+
*/
|
|
7
|
+
canEvict?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* If set to false, then no data will be prefilled into the component.
|
|
10
|
+
*/
|
|
11
|
+
initWithStoredValues?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* If set to true, data will be retrieved from cache during the first render even if there is a pending merge for the key.
|
|
14
|
+
*/
|
|
15
|
+
allowStaleData?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* This value will be returned by the hook on the first render while the data is being read from Onyx.
|
|
18
|
+
*/
|
|
19
|
+
initialValue?: TReturnValue;
|
|
20
|
+
/**
|
|
21
|
+
* This will be used to subscribe to a subset of an Onyx key's data.
|
|
22
|
+
* Using this setting on `useOnyx` can have very positive performance benefits because the component will only re-render
|
|
23
|
+
* when the subset of data changes. Otherwise, any change of data on any property would normally
|
|
24
|
+
* cause the component to re-render (and that can be expensive from a performance standpoint).
|
|
25
|
+
*/
|
|
26
|
+
selector?: Selector<TKey, unknown, TReturnValue>;
|
|
27
|
+
};
|
|
28
|
+
type FetchStatus = 'loading' | 'loaded';
|
|
29
|
+
type CachedValue<TKey extends OnyxKey, TValue> = IsEqual<TValue, OnyxValue<TKey>> extends true ? TValue : TKey extends CollectionKeyBase ? NonNullable<OnyxCollection<TValue>> : TValue;
|
|
30
|
+
type ResultMetadata = {
|
|
31
|
+
status: FetchStatus;
|
|
32
|
+
};
|
|
33
|
+
type UseOnyxResult<TKey extends OnyxKey, TValue> = [CachedValue<TKey, TValue>, ResultMetadata];
|
|
34
|
+
declare function useOnyx<TKey extends OnyxKey, TReturnValue = OnyxValue<TKey>>(key: TKey, options?: UseOnyxOptions<TKey, TReturnValue>): UseOnyxResult<TKey, TReturnValue>;
|
|
35
|
+
export default useOnyx;
|
|
36
|
+
export type { UseOnyxResult, FetchStatus };
|
package/dist/useOnyx.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fast_equals_1 = require("fast-equals");
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
const Onyx_1 = __importDefault(require("./Onyx"));
|
|
9
|
+
const useLiveRef_1 = __importDefault(require("./useLiveRef"));
|
|
10
|
+
const usePrevious_1 = __importDefault(require("./usePrevious"));
|
|
11
|
+
function getCachedValue(key, selector) {
|
|
12
|
+
return Onyx_1.default.tryGetCachedValue(key, { selector });
|
|
13
|
+
}
|
|
14
|
+
function useOnyx(key, options) {
|
|
15
|
+
const connectionIDRef = (0, react_1.useRef)(null);
|
|
16
|
+
const previousKey = (0, usePrevious_1.default)(key);
|
|
17
|
+
// Used to stabilize the selector reference and avoid unnecessary calls to `getSnapshot()`.
|
|
18
|
+
const selectorRef = (0, useLiveRef_1.default)(options === null || options === void 0 ? void 0 : options.selector);
|
|
19
|
+
// Stores the previous cached value as it's necessary to compare with the new value in `getSnapshot()`.
|
|
20
|
+
// We initialize it to `undefined` to simulate that we don't have any value from cache yet.
|
|
21
|
+
const cachedValueRef = (0, react_1.useRef)(undefined);
|
|
22
|
+
// Stores the previously result returned by the hook, containing the data from cache and the fetch status.
|
|
23
|
+
// We initialize it to `null` and `loading` fetch status to simulate the initial result when the hook is loading from the cache.
|
|
24
|
+
// However, if `initWithStoredValues` is `true` we set the fetch status to `loaded` since we want to signal that data is ready.
|
|
25
|
+
const resultRef = (0, react_1.useRef)([
|
|
26
|
+
null,
|
|
27
|
+
{
|
|
28
|
+
status: (options === null || options === void 0 ? void 0 : options.initWithStoredValues) === false ? 'loaded' : 'loading',
|
|
29
|
+
},
|
|
30
|
+
]);
|
|
31
|
+
// Indicates if it's the first Onyx connection of this hook or not, as we don't want certain use cases
|
|
32
|
+
// in `getSnapshot()` to be satisfied several times.
|
|
33
|
+
const isFirstConnectionRef = (0, react_1.useRef)(true);
|
|
34
|
+
(0, react_1.useEffect)(() => {
|
|
35
|
+
// These conditions will ensure we can only handle dynamic collection member keys from the same collection.
|
|
36
|
+
if (previousKey === key) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const previousCollectionKey = Onyx_1.default.splitCollectionMemberKey(previousKey)[0];
|
|
41
|
+
const collectionKey = Onyx_1.default.splitCollectionMemberKey(key)[0];
|
|
42
|
+
if (Onyx_1.default.isCollectionMemberKey(previousCollectionKey, previousKey) && Onyx_1.default.isCollectionMemberKey(collectionKey, key) && previousCollectionKey === collectionKey) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
throw new Error(`'${previousKey}' key can't be changed to '${key}'. useOnyx() only supports dynamic keys if they are both collection member keys from the same collection e.g. from 'collection_id1' to 'collection_id2'.`);
|
|
48
|
+
}
|
|
49
|
+
throw new Error(`'${previousKey}' key can't be changed to '${key}'. useOnyx() only supports dynamic keys if they are both collection member keys from the same collection e.g. from 'collection_id1' to 'collection_id2'.`);
|
|
50
|
+
}, [previousKey, key]);
|
|
51
|
+
const getSnapshot = (0, react_1.useCallback)(() => {
|
|
52
|
+
var _a;
|
|
53
|
+
// We get the value from the cache, supplying a selector too in case it's defined.
|
|
54
|
+
// If `newValue` is `undefined` it means that the cache doesn't have a value for that key yet.
|
|
55
|
+
// If `newValue` is `null` or any other value if means that the cache does have a value for that key.
|
|
56
|
+
// This difference between `undefined` and other values is crucial and it's used to address the following
|
|
57
|
+
// conditions and use cases.
|
|
58
|
+
let newValue = getCachedValue(key, selectorRef.current);
|
|
59
|
+
// Since the fetch status can be different given the use cases below, we define the variable right away.
|
|
60
|
+
let newFetchStatus;
|
|
61
|
+
// If we have pending merge operations for the key during the first connection, we set the new value to `undefined`
|
|
62
|
+
// and fetch status to `loading` to simulate that it is still being loaded until we have the most updated data.
|
|
63
|
+
// If `allowStaleData` is `true` this logic will be ignored and cached value will be used, even if it's stale data.
|
|
64
|
+
if (isFirstConnectionRef.current && Onyx_1.default.hasPendingMergeForKey(key) && !(options === null || options === void 0 ? void 0 : options.allowStaleData)) {
|
|
65
|
+
newValue = undefined;
|
|
66
|
+
newFetchStatus = 'loading';
|
|
67
|
+
}
|
|
68
|
+
// If data is not present in cache (if it's `undefined`) and `initialValue` is set during the first connection,
|
|
69
|
+
// we set the new value to `initialValue` and fetch status to `loaded` since we already have some data to return to the consumer.
|
|
70
|
+
if (isFirstConnectionRef.current && newValue === undefined && (options === null || options === void 0 ? void 0 : options.initialValue) !== undefined) {
|
|
71
|
+
newValue = options === null || options === void 0 ? void 0 : options.initialValue;
|
|
72
|
+
newFetchStatus = 'loaded';
|
|
73
|
+
}
|
|
74
|
+
// If the previously cached value is different from the new value, we update both cached value
|
|
75
|
+
// and the result to be returned by the hook.
|
|
76
|
+
if (!(0, fast_equals_1.deepEqual)(cachedValueRef.current, newValue)) {
|
|
77
|
+
cachedValueRef.current = newValue;
|
|
78
|
+
// If the new value is `undefined` we default it to `null` to ensure the consumer get a consistent result from the hook.
|
|
79
|
+
resultRef.current = [((_a = cachedValueRef.current) !== null && _a !== void 0 ? _a : null), { status: newFetchStatus !== null && newFetchStatus !== void 0 ? newFetchStatus : 'loaded' }];
|
|
80
|
+
}
|
|
81
|
+
return resultRef.current;
|
|
82
|
+
}, [key, selectorRef, options === null || options === void 0 ? void 0 : options.allowStaleData, options === null || options === void 0 ? void 0 : options.initialValue]);
|
|
83
|
+
const subscribe = (0, react_1.useCallback)((onStoreChange) => {
|
|
84
|
+
connectionIDRef.current = Onyx_1.default.connect({
|
|
85
|
+
key: key,
|
|
86
|
+
callback: () => {
|
|
87
|
+
// We don't need to update the Onyx cache again here, when `callback` is called the cache is already
|
|
88
|
+
// expected to be updated, so we just signal that the store changed and `getSnapshot()` can be called again.
|
|
89
|
+
isFirstConnectionRef.current = false;
|
|
90
|
+
onStoreChange();
|
|
91
|
+
},
|
|
92
|
+
initWithStoredValues: options === null || options === void 0 ? void 0 : options.initWithStoredValues,
|
|
93
|
+
waitForCollectionCallback: Onyx_1.default.isCollectionKey(key),
|
|
94
|
+
});
|
|
95
|
+
return () => {
|
|
96
|
+
if (!connectionIDRef.current) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
Onyx_1.default.disconnect(connectionIDRef.current);
|
|
100
|
+
isFirstConnectionRef.current = false;
|
|
101
|
+
};
|
|
102
|
+
}, [key, options === null || options === void 0 ? void 0 : options.initWithStoredValues]);
|
|
103
|
+
// Mimics withOnyx's checkEvictableKeys() behavior.
|
|
104
|
+
(0, react_1.useEffect)(() => {
|
|
105
|
+
if ((options === null || options === void 0 ? void 0 : options.canEvict) === undefined || !connectionIDRef.current) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (!Onyx_1.default.isSafeEvictionKey(key)) {
|
|
109
|
+
throw new Error(`canEvict can't be used on key '${key}'. This key must explicitly be flagged as safe for removal by adding it to Onyx.init({safeEvictionKeys: []}).`);
|
|
110
|
+
}
|
|
111
|
+
if (options.canEvict) {
|
|
112
|
+
Onyx_1.default.removeFromEvictionBlockList(key, connectionIDRef.current);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
Onyx_1.default.addToEvictionBlockList(key, connectionIDRef.current);
|
|
116
|
+
}
|
|
117
|
+
}, [key, options === null || options === void 0 ? void 0 : options.canEvict]);
|
|
118
|
+
const result = (0, react_1.useSyncExternalStore)(subscribe, getSnapshot);
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
exports.default = useOnyx;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const react_1 = require("react");
|
|
4
|
+
/**
|
|
5
|
+
* Returns the previous value of the provided value.
|
|
6
|
+
*/
|
|
7
|
+
function usePrevious(value) {
|
|
8
|
+
const ref = (0, react_1.useRef)(value);
|
|
9
|
+
(0, react_1.useEffect)(() => {
|
|
10
|
+
ref.current = value;
|
|
11
|
+
}, [value]);
|
|
12
|
+
return ref.current;
|
|
13
|
+
}
|
|
14
|
+
exports.default = usePrevious;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-onyx",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.18",
|
|
4
4
|
"author": "Expensify, Inc.",
|
|
5
5
|
"homepage": "https://expensify.com",
|
|
6
6
|
"description": "State management for React Native",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"typecheck": "tsc --noEmit",
|
|
33
33
|
"test": "jest",
|
|
34
34
|
"build": "tsc -p tsconfig.build.json && cp ./lib/*.d.ts ./dist",
|
|
35
|
-
"build:watch": "nodemon --watch lib --exec \"npm run build && npm pack\"",
|
|
35
|
+
"build:watch": "nodemon --watch lib --ext js,json,ts,tsx --exec \"npm run build && npm pack\"",
|
|
36
36
|
"build:docs": "node buildDocs.js",
|
|
37
37
|
"lint-tests": "eslint tests/**",
|
|
38
38
|
"prettier": "prettier --write ."
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"@react-native-community/eslint-config": "^3.2.0",
|
|
48
48
|
"@react-native/polyfills": "^2.0.0",
|
|
49
49
|
"@testing-library/jest-native": "^3.4.2",
|
|
50
|
-
"@testing-library/react-native": "^
|
|
50
|
+
"@testing-library/react-native": "^10.0.0",
|
|
51
51
|
"@types/jest": "^28.1.8",
|
|
52
52
|
"@types/lodash": "^4.14.202",
|
|
53
53
|
"@types/node": "^20.11.5",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"@typescript-eslint/eslint-plugin": "^6.19.0",
|
|
59
59
|
"@typescript-eslint/parser": "^6.19.0",
|
|
60
60
|
"eslint": "^8.56.0",
|
|
61
|
-
"eslint-config-expensify": "^2.0.
|
|
61
|
+
"eslint-config-expensify": "^2.0.44",
|
|
62
62
|
"eslint-config-prettier": "^8.8.0",
|
|
63
63
|
"eslint-plugin-import": "^2.29.1",
|
|
64
64
|
"eslint-plugin-jsx-a11y": "^6.8.0",
|