@tanstack/react-optimistic
Version:
React hooks for optimistic updates
166 lines (165 loc) • 5.23 kB
JavaScript
;
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