UNPKG

@tanstack/react-optimistic

Version:

React hooks for optimistic updates

166 lines (165 loc) 5.23 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const withSelector_js = require("use-sync-external-store/shim/with-selector.js"); const optimistic = require("@tanstack/optimistic"); let snapshotCache = null; function useCollections() { return withSelector_js.useSyncExternalStoreWithSelector( (callback) => { const storeUnsubscribe = optimistic.collectionsStore.subscribe(() => { snapshotCache = null; callback(); }); const collectionUnsubscribes = Array.from( optimistic.collectionsStore.state.values() ).map((collection) => { const derivedStateUnsub = collection.derivedState.subscribe(() => { snapshotCache = null; callback(); }); return derivedStateUnsub; }); return () => { storeUnsubscribe(); collectionUnsubscribes.forEach((unsubscribe) => unsubscribe()); }; }, // Get snapshot of all collections and their transactions () => { if (snapshotCache) { return snapshotCache; } const snapshot = /* @__PURE__ */ new Map(); for (const [id, collection] of optimistic.collectionsStore.state) { snapshot.set(id, { state: collection.derivedState.state, transactions: collection.transactions.state }); } snapshotCache = snapshot; return snapshot; }, // Server snapshot (same as client for now) () => { if (snapshotCache) { return snapshotCache; } const snapshot = /* @__PURE__ */ new Map(); for (const [id, collection] of optimistic.collectionsStore.state) { snapshot.set(id, { state: collection.derivedState.state, transactions: collection.transactions.state }); } snapshotCache = snapshot; return snapshot; }, // Identity selector (state) => state, // Custom equality function for Maps (a, b) => { if (a === b) return true; if (!(a instanceof Map) || !(b instanceof Map)) return false; if (a.size !== b.size) return false; for (const [key, value] of a) { const bValue = b.get(key); if (!b.has(key)) return false; if (value instanceof Map) { if (!(bValue instanceof Map)) return false; if (!shallow(value, bValue)) return false; } else if (!Object.is(value, bValue)) { return false; } } return true; } ); } function useCollection(config, selector) { if (selector) { console.log(`selector support not yet implemented`, selector); } if (!optimistic.collectionsStore.state.has(config.id)) { optimistic.collectionsStore.setState((prev) => { const next = new Map(prev); next.set( config.id, new optimistic.Collection({ id: config.id, sync: config.sync, schema: config.schema }) ); return next; }); } const collection = optimistic.collectionsStore.state.get(config.id); const result = withSelector_js.useSyncExternalStoreWithSelector( collection.derivedState.subscribe, () => collection.derivedState.state, () => collection.derivedState.state, (stateMap) => { return { state: stateMap, // derivedState & derivedArray are recomputed at the same time. data: collection.derivedArray.state }; }, (a, b) => { if (a === b) return true; const stateEqual = a.state.size === b.state.size && Array.from(a.state.keys()).every( (key) => shallow(a.state.get(key), b.state.get(key)) ); const dataEqual = a.data.length === b.data.length && a.data.every((datum, i) => shallow(datum, b.data[i])); return stateEqual && dataEqual; } ); const returnValue = { state: result.state, data: result.data, insert: collection.insert.bind(collection), update: collection.update.bind(collection), delete: collection.delete.bind(collection) }; return returnValue; } function shallow(objA, objB) { if (Object.is(objA, objB)) { return true; } if (typeof objA !== `object` || objA === null || typeof objB !== `object` || objB === null) { return false; } if (objA instanceof Map && objB instanceof Map) { if (objA.size !== objB.size) return false; for (const [k, v] of objA) { if (!objB.has(k) || !Object.is(v, objB.get(k))) return false; } return true; } if (objA instanceof Set && objB instanceof Set) { if (objA.size !== objB.size) return false; for (const v of objA) { if (!objB.has(v)) return false; } return true; } const keysA = Object.keys(objA); if (keysA.length !== Object.keys(objB).length) { return false; } for (const key of keysA) { if (!Object.prototype.hasOwnProperty.call(objB, key) || !Object.is(objA[key], objB[key])) { return false; } } return true; } Object.defineProperty(exports, "preloadCollection", { enumerable: true, get: () => optimistic.preloadCollection }); exports.shallow = shallow; exports.useCollection = useCollection; exports.useCollections = useCollections; //# sourceMappingURL=useCollection.cjs.map