react-native-onyx 3.0.94 → 3.0.96
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/dist/Onyx.d.ts +1 -1
- package/dist/Onyx.js +42 -11
- package/dist/createMemoizedSelector.d.ts +18 -0
- package/dist/createMemoizedSelector.js +37 -0
- package/dist/storage/InstanceSync/index.web.d.ts +17 -11
- package/dist/storage/InstanceSync/index.web.js +110 -10
- package/dist/storage/index.js +3 -3
- package/dist/storage/providers/IDBKeyValProvider/classifyError.js +6 -0
- package/dist/storage/providers/types.d.ts +5 -4
- package/dist/useOnyx.d.ts +1 -2
- package/dist/useOnyx.js +6 -49
- package/package.json +1 -1
- package/dist/useLiveRef.d.ts +0 -9
- package/dist/useLiveRef.js +0 -16
package/dist/Onyx.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as Logger from './Logger';
|
|
2
|
-
import type { CollectionKeyBase, ConnectOptions, InitOptions, OnyxKey, OnyxMergeCollectionInput,
|
|
2
|
+
import type { CollectionKeyBase, ConnectOptions, InitOptions, OnyxKey, OnyxMergeCollectionInput, OnyxMergeInput, OnyxMultiSetInput, OnyxSetCollectionInput, OnyxSetInput, OnyxUpdate, SetOptions } from './types';
|
|
3
3
|
import type { Connection } from './OnyxConnectionManager';
|
|
4
4
|
/** Initialize the store with actions and listening for storage events */
|
|
5
5
|
declare function init({ keys, initialKeyStates, evictableKeys, shouldSyncMultipleInstances, enableDevTools, skippableCollectionMemberIDs, ramOnlyKeys, snapshotMergeKeys, }: InitOptions): void;
|
package/dist/Onyx.js
CHANGED
|
@@ -55,18 +55,49 @@ function init({ keys = {}, initialKeyStates = {}, evictableKeys = [], shouldSync
|
|
|
55
55
|
OnyxUtils_1.default.setSnapshotMergeKeys(new Set(snapshotMergeKeys));
|
|
56
56
|
OnyxKeys_1.default.setRamOnlyKeys(new Set(ramOnlyKeys));
|
|
57
57
|
if (shouldSyncMultipleInstances) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
// Cross-tab sync (InstanceSync) hands us the full batch of key/value pairs that changed together in
|
|
59
|
+
// a single write. We process it synchronously, grouping collection members so each affected
|
|
60
|
+
// collection is notified once (mirroring the local mergeCollection batching) instead of
|
|
61
|
+
// re-delivering the whole collection per member.
|
|
62
|
+
(_a = storage_1.default.keepInstancesSync) === null || _a === void 0 ? void 0 : _a.call(storage_1.default, (pairs) => {
|
|
63
|
+
const individual = [];
|
|
64
|
+
const collectionBatches = new Map();
|
|
65
|
+
for (const [key, value] of pairs) {
|
|
66
|
+
// RAM-only keys should never sync from storage as they may have stale persisted data
|
|
67
|
+
// from before the key was migrated to RAM-only.
|
|
68
|
+
if (OnyxKeys_1.default.isRamOnlyKey(key)) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const collectionKey = OnyxKeys_1.default.getCollectionKey(key);
|
|
72
|
+
const isCollectionMember = !!collectionKey && OnyxKeys_1.default.isCollectionMemberKey(collectionKey, key);
|
|
73
|
+
// Capture the previous cached value BEFORE cache.set() so keysChanged() can diff old vs new per member.
|
|
74
|
+
const previousValue = isCollectionMember ? OnyxCache_1.default.get(key) : undefined;
|
|
75
|
+
OnyxCache_1.default.set(key, value);
|
|
76
|
+
if (isCollectionMember && collectionKey) {
|
|
77
|
+
let batch = collectionBatches.get(collectionKey);
|
|
78
|
+
if (!batch) {
|
|
79
|
+
batch = { partial: {}, previous: {} };
|
|
80
|
+
collectionBatches.set(collectionKey, batch);
|
|
81
|
+
}
|
|
82
|
+
batch.partial[key] = value;
|
|
83
|
+
// Keep the earliest previous value in case the same member appears twice in one batch.
|
|
84
|
+
if (!(key in batch.previous)) {
|
|
85
|
+
batch.previous[key] = previousValue;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
individual.push([key, value]);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Non-collection keys: notify individually, matching keyChanged() semantics for exact keys.
|
|
93
|
+
for (const [key, value] of individual) {
|
|
94
|
+
OnyxUtils_1.default.keyChanged(key, value);
|
|
95
|
+
}
|
|
96
|
+
// One keysChanged() per collection notifies the collection-root subscriber once and lets
|
|
97
|
+
// keysChanged() decide which individual member subscribers actually changed.
|
|
98
|
+
for (const [collectionKey, { partial, previous }] of collectionBatches) {
|
|
99
|
+
OnyxUtils_1.default.keysChanged(collectionKey, partial, previous);
|
|
63
100
|
}
|
|
64
|
-
OnyxCache_1.default.set(key, value);
|
|
65
|
-
// Check if this is a collection member key to prevent duplicate callbacks
|
|
66
|
-
// When a collection is updated, individual members sync separately to other tabs
|
|
67
|
-
// Setting isProcessingCollectionUpdate=true prevents triggering collection callbacks for each individual update
|
|
68
|
-
const isKeyCollectionMember = OnyxKeys_1.default.isCollectionMember(key);
|
|
69
|
-
OnyxUtils_1.default.keyChanged(key, value, undefined, isKeyCollectionMember);
|
|
70
101
|
});
|
|
71
102
|
}
|
|
72
103
|
OnyxUtils_1.default.initStoreValues(keys, initialKeyStates, evictableKeys);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wraps a selector function so that:
|
|
3
|
+
* - Calling the wrapper with the same input reference twice short-circuits to the cached output
|
|
4
|
+
* (cheap `===` check, no recompute).
|
|
5
|
+
* - Calling with a different input that produces a deep-equal output returns the *previous*
|
|
6
|
+
* output reference, so downstream `===` comparisons treat it as unchanged.
|
|
7
|
+
*
|
|
8
|
+
* This is the minimum needed for `useSyncExternalStore` to not loop when consumers pass
|
|
9
|
+
* inline selectors that allocate fresh objects on every call (e.g. `(e) => ({id: e?.id})`):
|
|
10
|
+
* without the deep-equal fallback, every `getSnapshot` would return a new reference and React
|
|
11
|
+
* would re-render (or throw "getSnapshot should be cached") indefinitely.
|
|
12
|
+
*
|
|
13
|
+
* Stateful by design — each call to `createMemoizedSelector` produces an independent wrapper
|
|
14
|
+
* with its own `lastInput`/`lastOutput` cache, so a wrapper must not be shared across
|
|
15
|
+
* subscriptions that can see different inputs.
|
|
16
|
+
*/
|
|
17
|
+
declare function createMemoizedSelector<TInput, TOutput>(selector: (input: TInput) => TOutput): (input: TInput) => TOutput;
|
|
18
|
+
export default createMemoizedSelector;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const fast_equals_1 = require("fast-equals");
|
|
4
|
+
/**
|
|
5
|
+
* Wraps a selector function so that:
|
|
6
|
+
* - Calling the wrapper with the same input reference twice short-circuits to the cached output
|
|
7
|
+
* (cheap `===` check, no recompute).
|
|
8
|
+
* - Calling with a different input that produces a deep-equal output returns the *previous*
|
|
9
|
+
* output reference, so downstream `===` comparisons treat it as unchanged.
|
|
10
|
+
*
|
|
11
|
+
* This is the minimum needed for `useSyncExternalStore` to not loop when consumers pass
|
|
12
|
+
* inline selectors that allocate fresh objects on every call (e.g. `(e) => ({id: e?.id})`):
|
|
13
|
+
* without the deep-equal fallback, every `getSnapshot` would return a new reference and React
|
|
14
|
+
* would re-render (or throw "getSnapshot should be cached") indefinitely.
|
|
15
|
+
*
|
|
16
|
+
* Stateful by design — each call to `createMemoizedSelector` produces an independent wrapper
|
|
17
|
+
* with its own `lastInput`/`lastOutput` cache, so a wrapper must not be shared across
|
|
18
|
+
* subscriptions that can see different inputs.
|
|
19
|
+
*/
|
|
20
|
+
function createMemoizedSelector(selector) {
|
|
21
|
+
let lastInput;
|
|
22
|
+
let lastOutput;
|
|
23
|
+
let hasComputed = false;
|
|
24
|
+
return (input) => {
|
|
25
|
+
if (hasComputed && lastInput === input) {
|
|
26
|
+
return lastOutput;
|
|
27
|
+
}
|
|
28
|
+
const next = selector(input);
|
|
29
|
+
lastInput = input;
|
|
30
|
+
if (!hasComputed || !(0, fast_equals_1.deepEqual)(lastOutput, next)) {
|
|
31
|
+
lastOutput = next;
|
|
32
|
+
hasComputed = true;
|
|
33
|
+
}
|
|
34
|
+
return lastOutput;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
exports.default = createMemoizedSelector;
|
|
@@ -1,23 +1,29 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The InstancesSync object provides data-changed events like the ones that exist
|
|
3
|
-
* when using LocalStorage APIs in the browser. These events are great because multiple tabs can listen for when
|
|
4
|
-
* data changes and then stay up-to-date with everything happening in Onyx.
|
|
5
|
-
*/
|
|
6
1
|
import type { OnyxKey } from '../../types';
|
|
7
|
-
import type { StorageKeyList,
|
|
2
|
+
import type { StorageKeyList, OnStorageKeysChanged } from '../providers/types';
|
|
8
3
|
import type StorageProvider from '../providers/types';
|
|
9
4
|
/**
|
|
10
|
-
* Raise
|
|
11
|
-
*
|
|
5
|
+
* Raise cross-tab event(s) for a batch of changed keys. Sending keys together (instead of one event per
|
|
6
|
+
* key) preserves the write's batch boundary across tabs, so the receiving tab notifies collection
|
|
7
|
+
* subscribers once for the whole batch — matching the local mergeCollection behavior — instead of
|
|
8
|
+
* re-delivering the whole collection once per member (O(N^2), which can crash the tab). Large batches are
|
|
9
|
+
* chunked so no single payload approaches the localStorage quota.
|
|
12
10
|
*/
|
|
13
|
-
declare function raiseStorageSyncEvent(onyxKey: OnyxKey): void;
|
|
14
11
|
declare function raiseStorageSyncManyKeysEvent(onyxKeys: StorageKeyList): void;
|
|
12
|
+
/**
|
|
13
|
+
* Raise an event through `localStorage` to let other tabs know a single key changed.
|
|
14
|
+
*
|
|
15
|
+
* This intentionally emits the raw key (the legacy, pre-batching format) rather than a JSON array, so a
|
|
16
|
+
* tab still running the previous bundle during a deploy keeps receiving single-key updates (a new message,
|
|
17
|
+
* a pin, a rename, etc.). Only multi-key writes use the batched JSON-array format; the receiver here
|
|
18
|
+
* understands both. The mixed-version gap is therefore limited to bulk collection writes, which resolve on reload.
|
|
19
|
+
*/
|
|
20
|
+
declare function raiseStorageSyncEvent(onyxKey: OnyxKey): void;
|
|
15
21
|
declare const InstanceSync: {
|
|
16
22
|
shouldBeUsed: boolean;
|
|
17
23
|
/**
|
|
18
|
-
* @param {Function}
|
|
24
|
+
* @param {Function} onStorageKeysChanged Storage synchronization mechanism keeping all opened tabs in sync
|
|
19
25
|
*/
|
|
20
|
-
init: (
|
|
26
|
+
init: (onStorageKeysChanged: OnStorageKeysChanged, store: StorageProvider<unknown>) => void;
|
|
21
27
|
setItem: typeof raiseStorageSyncEvent;
|
|
22
28
|
removeItem: typeof raiseStorageSyncEvent;
|
|
23
29
|
removeItems: typeof raiseStorageSyncManyKeysEvent;
|
|
@@ -1,30 +1,130 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
/**
|
|
40
|
+
* The InstancesSync object provides data-changed events like the ones that exist
|
|
41
|
+
* when using LocalStorage APIs in the browser. These events are great because multiple tabs can listen for when
|
|
42
|
+
* data changes and then stay up-to-date with everything happening in Onyx.
|
|
43
|
+
*/
|
|
44
|
+
const Logger = __importStar(require("../../Logger"));
|
|
6
45
|
const NoopProvider_1 = __importDefault(require("../providers/NoopProvider"));
|
|
7
46
|
const SYNC_ONYX = 'SYNC_ONYX';
|
|
47
|
+
// localStorage stores values as UTF-16 (~2 bytes/char). The per-origin quota isn't fixed by the spec —
|
|
48
|
+
// it's user-agent dependent and commonly ~5MB — so we keep each SYNC_ONYX payload conservatively small
|
|
49
|
+
// (and pair it with a try/catch in emitSyncEvent). This way a large key batch (e.g. Onyx.clear() on a
|
|
50
|
+
// heavy account, or a bulk import) is split across several events instead of throwing QuotaExceededError.
|
|
51
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
|
|
52
|
+
const MAX_SYNC_PAYLOAD_LENGTH = 1000000;
|
|
8
53
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
54
|
+
* Parses the SYNC_ONYX storage event value.
|
|
55
|
+
* The payload is a JSON array of the changed keys (a batch). It falls back to treating the raw
|
|
56
|
+
* value as a single key for backwards compatibility with the previous one-key-per-event format.
|
|
11
57
|
*/
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
|
|
58
|
+
function parseSyncOnyxStorageEventValue(value) {
|
|
59
|
+
let onyxKeys;
|
|
60
|
+
try {
|
|
61
|
+
const parsed = JSON.parse(value);
|
|
62
|
+
onyxKeys = Array.isArray(parsed) ? parsed : [value];
|
|
63
|
+
}
|
|
64
|
+
catch (_a) {
|
|
65
|
+
onyxKeys = [value];
|
|
66
|
+
}
|
|
67
|
+
return onyxKeys;
|
|
15
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Emit a single SYNC_ONYX storage event. Wrapped so a failed cross-tab signal
|
|
71
|
+
* degrades gracefully — other tabs simply miss this update until their next organic sync/reload — instead
|
|
72
|
+
* of throwing an uncaught rejection in the writing tab.
|
|
73
|
+
*/
|
|
74
|
+
function emitSyncEvent(value) {
|
|
75
|
+
try {
|
|
76
|
+
global.localStorage.setItem(SYNC_ONYX, value);
|
|
77
|
+
global.localStorage.removeItem(SYNC_ONYX);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
Logger.logAlert(`[InstanceSync] Failed to raise storage sync event: ${error}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Raise cross-tab event(s) for a batch of changed keys. Sending keys together (instead of one event per
|
|
85
|
+
* key) preserves the write's batch boundary across tabs, so the receiving tab notifies collection
|
|
86
|
+
* subscribers once for the whole batch — matching the local mergeCollection behavior — instead of
|
|
87
|
+
* re-delivering the whole collection once per member (O(N^2), which can crash the tab). Large batches are
|
|
88
|
+
* chunked so no single payload approaches the localStorage quota.
|
|
89
|
+
*/
|
|
16
90
|
function raiseStorageSyncManyKeysEvent(onyxKeys) {
|
|
91
|
+
if (onyxKeys.length === 0) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
let chunk = [];
|
|
95
|
+
let chunkLength = 2; // accounts for the surrounding `[]`
|
|
17
96
|
for (const onyxKey of onyxKeys) {
|
|
18
|
-
|
|
97
|
+
const keyLength = onyxKey.length + 3; // quotes + comma separator
|
|
98
|
+
if (chunk.length > 0 && chunkLength + keyLength > MAX_SYNC_PAYLOAD_LENGTH) {
|
|
99
|
+
emitSyncEvent(JSON.stringify(chunk));
|
|
100
|
+
chunk = [];
|
|
101
|
+
chunkLength = 2;
|
|
102
|
+
}
|
|
103
|
+
chunk.push(onyxKey);
|
|
104
|
+
chunkLength += keyLength;
|
|
105
|
+
}
|
|
106
|
+
if (chunk.length > 0) {
|
|
107
|
+
emitSyncEvent(JSON.stringify(chunk));
|
|
19
108
|
}
|
|
20
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Raise an event through `localStorage` to let other tabs know a single key changed.
|
|
112
|
+
*
|
|
113
|
+
* This intentionally emits the raw key (the legacy, pre-batching format) rather than a JSON array, so a
|
|
114
|
+
* tab still running the previous bundle during a deploy keeps receiving single-key updates (a new message,
|
|
115
|
+
* a pin, a rename, etc.). Only multi-key writes use the batched JSON-array format; the receiver here
|
|
116
|
+
* understands both. The mixed-version gap is therefore limited to bulk collection writes, which resolve on reload.
|
|
117
|
+
*/
|
|
118
|
+
function raiseStorageSyncEvent(onyxKey) {
|
|
119
|
+
emitSyncEvent(onyxKey);
|
|
120
|
+
}
|
|
21
121
|
let storage = NoopProvider_1.default;
|
|
22
122
|
const InstanceSync = {
|
|
23
123
|
shouldBeUsed: true,
|
|
24
124
|
/**
|
|
25
|
-
* @param {Function}
|
|
125
|
+
* @param {Function} onStorageKeysChanged Storage synchronization mechanism keeping all opened tabs in sync
|
|
26
126
|
*/
|
|
27
|
-
init: (
|
|
127
|
+
init: (onStorageKeysChanged, store) => {
|
|
28
128
|
storage = store;
|
|
29
129
|
// This listener will only be triggered by events coming from other tabs
|
|
30
130
|
global.addEventListener('storage', (event) => {
|
|
@@ -32,8 +132,8 @@ const InstanceSync = {
|
|
|
32
132
|
if (event.key !== SYNC_ONYX || !event.newValue) {
|
|
33
133
|
return;
|
|
34
134
|
}
|
|
35
|
-
const
|
|
36
|
-
storage.
|
|
135
|
+
const onyxKeys = parseSyncOnyxStorageEventValue(event.newValue);
|
|
136
|
+
storage.multiGet(onyxKeys).then((pairs) => onStorageKeysChanged(pairs));
|
|
37
137
|
});
|
|
38
138
|
},
|
|
39
139
|
setItem: raiseStorageSyncEvent,
|
package/dist/storage/index.js
CHANGED
|
@@ -181,14 +181,14 @@ const storage = {
|
|
|
181
181
|
*/
|
|
182
182
|
getDatabaseSize: () => tryOrDegradePerformance(() => provider.getDatabaseSize()),
|
|
183
183
|
/**
|
|
184
|
-
* @param
|
|
184
|
+
* @param onStorageKeysChanged - Storage synchronization mechanism keeping all opened tabs in sync (web only)
|
|
185
185
|
*/
|
|
186
|
-
keepInstancesSync(
|
|
186
|
+
keepInstancesSync(onStorageKeysChanged) {
|
|
187
187
|
// If InstanceSync shouldn't be used, it means we're on a native platform and we don't need to keep instances in sync
|
|
188
188
|
if (!InstanceSync_1.default.shouldBeUsed)
|
|
189
189
|
return;
|
|
190
190
|
shouldKeepInstancesSync = true;
|
|
191
|
-
InstanceSync_1.default.init(
|
|
191
|
+
InstanceSync_1.default.init(onStorageKeysChanged, this);
|
|
192
192
|
},
|
|
193
193
|
};
|
|
194
194
|
exports.default = storage;
|
|
@@ -12,6 +12,12 @@ function classifyIDBError(error) {
|
|
|
12
12
|
if (message.includes("failed to execute 'put' on 'idbobjectstore'")) {
|
|
13
13
|
return errors_1.StorageErrorClass.INVALID_DATA;
|
|
14
14
|
}
|
|
15
|
+
// A queued File/Blob whose backing bytes are gone — the source OS file was modified, deleted, or
|
|
16
|
+
// renamed after being picked, so the structured clone fails at write time. Chromium reports it as
|
|
17
|
+
// InvalidBlob (Windows) or IOError (macOS). Retrying re-reads the same dead blob and can never succeed.
|
|
18
|
+
if (message.includes('failed to write blobs')) {
|
|
19
|
+
return errors_1.StorageErrorClass.INVALID_DATA;
|
|
20
|
+
}
|
|
15
21
|
// Browser quota exceeded.
|
|
16
22
|
if (name.includes('quotaexceedederror') || message.includes('quotaexceedederror')) {
|
|
17
23
|
return errors_1.StorageErrorClass.CAPACITY;
|
|
@@ -9,7 +9,8 @@ type DatabaseSize = {
|
|
|
9
9
|
bytesRemaining: number;
|
|
10
10
|
usageDetails?: Record<string, number>;
|
|
11
11
|
};
|
|
12
|
-
|
|
12
|
+
/** Called with the full batch of key/value pairs that changed together in a single cross-tab sync event. */
|
|
13
|
+
type OnStorageKeysChanged = (pairs: StorageKeyValuePair[]) => void;
|
|
13
14
|
type StorageProvider<TStore> = {
|
|
14
15
|
store: TStore;
|
|
15
16
|
/**
|
|
@@ -77,9 +78,9 @@ type StorageProvider<TStore> = {
|
|
|
77
78
|
*/
|
|
78
79
|
classifyError: (error: unknown) => ValueOf<typeof StorageErrorClass>;
|
|
79
80
|
/**
|
|
80
|
-
* @param
|
|
81
|
+
* @param onStorageKeysChanged Storage synchronization mechanism keeping all opened tabs in sync
|
|
81
82
|
*/
|
|
82
|
-
keepInstancesSync?: (
|
|
83
|
+
keepInstancesSync?: (onStorageKeysChanged: OnStorageKeysChanged) => void;
|
|
83
84
|
};
|
|
84
85
|
export default StorageProvider;
|
|
85
|
-
export type { StorageKeyList, StorageKeyValuePair,
|
|
86
|
+
export type { StorageKeyList, StorageKeyValuePair, OnStorageKeysChanged };
|
package/dist/useOnyx.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { DependencyList } from 'react';
|
|
2
1
|
import type { OnyxKey, OnyxValue } from './types';
|
|
3
2
|
type UseOnyxSelector<TKey extends OnyxKey, TReturnValue = OnyxValue<TKey>> = (data: OnyxValue<TKey> | undefined) => TReturnValue;
|
|
4
3
|
type UseOnyxOptions<TKey extends OnyxKey, TReturnValue> = {
|
|
@@ -21,6 +20,6 @@ type ResultMetadata = {
|
|
|
21
20
|
status: FetchStatus;
|
|
22
21
|
};
|
|
23
22
|
type UseOnyxResult<TValue> = [NonNullable<TValue> | undefined, ResultMetadata];
|
|
24
|
-
declare function useOnyx<TKey extends OnyxKey, TReturnValue = OnyxValue<TKey>>(key: TKey, options?: UseOnyxOptions<TKey, TReturnValue
|
|
23
|
+
declare function useOnyx<TKey extends OnyxKey, TReturnValue = OnyxValue<TKey>>(key: TKey, options?: UseOnyxOptions<TKey, TReturnValue>): UseOnyxResult<TReturnValue>;
|
|
25
24
|
export default useOnyx;
|
|
26
25
|
export type { FetchStatus, ResultMetadata, UseOnyxResult, UseOnyxOptions, UseOnyxSelector };
|
package/dist/useOnyx.js
CHANGED
|
@@ -36,46 +36,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
const fast_equals_1 = require("fast-equals");
|
|
40
39
|
const react_1 = require("react");
|
|
40
|
+
const createMemoizedSelector_1 = __importDefault(require("./createMemoizedSelector"));
|
|
41
41
|
const OnyxCache_1 = __importStar(require("./OnyxCache"));
|
|
42
42
|
const OnyxConnectionManager_1 = __importDefault(require("./OnyxConnectionManager"));
|
|
43
43
|
const OnyxUtils_1 = __importDefault(require("./OnyxUtils"));
|
|
44
44
|
const OnyxSnapshotCache_1 = __importDefault(require("./OnyxSnapshotCache"));
|
|
45
45
|
const memoizedShallowEqual_1 = __importDefault(require("./memoizedShallowEqual"));
|
|
46
|
-
|
|
47
|
-
function useOnyx(key, options, dependencies = []) {
|
|
46
|
+
function useOnyx(key, options) {
|
|
48
47
|
const connectionRef = (0, react_1.useRef)(null);
|
|
49
|
-
const currentDependenciesRef = (0, useLiveRef_1.default)(dependencies);
|
|
50
48
|
const selector = options === null || options === void 0 ? void 0 : options.selector;
|
|
51
|
-
// Create memoized version of selector for performance
|
|
49
|
+
// Create memoized version of selector for performance. It caches by input reference
|
|
50
|
+
// with a deepEqual fallback on the output to keep the returned reference stable.
|
|
52
51
|
const memoizedSelector = (0, react_1.useMemo)(() => {
|
|
53
52
|
if (!selector) {
|
|
54
53
|
return null;
|
|
55
54
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
let lastDependencies = [];
|
|
59
|
-
let hasComputed = false;
|
|
60
|
-
return (input) => {
|
|
61
|
-
const currentDependencies = currentDependenciesRef.current;
|
|
62
|
-
// Recompute if input changed, dependencies changed, or first time
|
|
63
|
-
const dependenciesChanged = !(0, fast_equals_1.shallowEqual)(lastDependencies, currentDependencies);
|
|
64
|
-
if (!hasComputed || lastInput !== input || dependenciesChanged) {
|
|
65
|
-
const newOutput = selector(input);
|
|
66
|
-
// Always track the current input to avoid re-running the selector
|
|
67
|
-
// when the same input is seen again (even if the output didn't change).
|
|
68
|
-
lastInput = input;
|
|
69
|
-
// Only update the output reference if it actually changed
|
|
70
|
-
if (!hasComputed || !(0, fast_equals_1.deepEqual)(lastOutput, newOutput) || dependenciesChanged) {
|
|
71
|
-
lastOutput = newOutput;
|
|
72
|
-
lastDependencies = [...currentDependencies];
|
|
73
|
-
hasComputed = true;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return lastOutput;
|
|
77
|
-
};
|
|
78
|
-
}, [currentDependenciesRef, selector]);
|
|
55
|
+
return (0, createMemoizedSelector_1.default)(selector);
|
|
56
|
+
}, [selector]);
|
|
79
57
|
// Stores the previous cached value as it's necessary to compare with the new value in `getSnapshot()`.
|
|
80
58
|
// We initialize it to `null` to simulate that we don't have any value from cache yet.
|
|
81
59
|
const previousValueRef = (0, react_1.useRef)(null);
|
|
@@ -109,27 +87,6 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
109
87
|
selector: options === null || options === void 0 ? void 0 : options.selector,
|
|
110
88
|
}), [key, options === null || options === void 0 ? void 0 : options.selector]);
|
|
111
89
|
(0, react_1.useEffect)(() => () => OnyxSnapshotCache_1.default.deregisterConsumer(key, cacheKey), [key, cacheKey]);
|
|
112
|
-
// Track previous dependencies to prevent infinite loops
|
|
113
|
-
const previousDependenciesRef = (0, react_1.useRef)([]);
|
|
114
|
-
(0, react_1.useEffect)(() => {
|
|
115
|
-
// This effect will only run if the `dependencies` array changes. If it changes it will force the hook
|
|
116
|
-
// to trigger a `getSnapshot()` update by calling the stored `onStoreChange()` function reference, thus
|
|
117
|
-
// re-running the hook and returning the latest value to the consumer.
|
|
118
|
-
// Deep equality check to prevent infinite loops when dependencies array reference changes
|
|
119
|
-
// but content remains the same
|
|
120
|
-
if ((0, fast_equals_1.shallowEqual)(previousDependenciesRef.current, dependencies)) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
previousDependenciesRef.current = dependencies;
|
|
124
|
-
if (connectionRef.current === null || isConnectingRef.current || connectedKeyRef.current !== key || !onStoreChangeFnRef.current) {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
// Invalidate cache when dependencies change so selector runs with new closure values
|
|
128
|
-
OnyxSnapshotCache_1.default.invalidateForKey(key);
|
|
129
|
-
shouldGetCachedValueRef.current = true;
|
|
130
|
-
onStoreChangeFnRef.current();
|
|
131
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
132
|
-
}, [...dependencies]);
|
|
133
90
|
// Tracks the last memoizedSelector reference that getSnapshot() has computed with.
|
|
134
91
|
// When the selector changes, this mismatch forces getSnapshot() to re-evaluate
|
|
135
92
|
// even if all other conditions (isFirstConnection, shouldGetCachedValue, key) are false.
|
package/package.json
CHANGED
package/dist/useLiveRef.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Creates a mutable reference to a value, useful when you need to
|
|
3
|
-
* maintain a reference to a value that may change over time without triggering re-renders.
|
|
4
|
-
*
|
|
5
|
-
* @deprecated This hook breaks the Rules of React, and should not be used.
|
|
6
|
-
* The migration effort to remove it safely is not currently planned.
|
|
7
|
-
*/
|
|
8
|
-
declare function useLiveRef<T>(value: T): import("react").MutableRefObject<T>;
|
|
9
|
-
export default useLiveRef;
|
package/dist/useLiveRef.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
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
|
-
* @deprecated This hook breaks the Rules of React, and should not be used.
|
|
9
|
-
* The migration effort to remove it safely is not currently planned.
|
|
10
|
-
*/
|
|
11
|
-
function useLiveRef(value) {
|
|
12
|
-
const ref = (0, react_1.useRef)(value);
|
|
13
|
-
ref.current = value;
|
|
14
|
-
return ref;
|
|
15
|
-
}
|
|
16
|
-
exports.default = useLiveRef;
|