dexie-react-hooks
Version:
React hooks for reactive data fetching using Dexie.js
159 lines (150 loc) • 7.66 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('dexie'), require('react')) :
typeof define === 'function' && define.amd ? define(['exports', 'dexie', 'react'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.DexieReactHooks = {}, global.Dexie, global.React));
})(this, (function (exports, dexie, React) { 'use strict';
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
function useObservable(observableFactory, arg2, arg3) {
// Resolve vars from overloading variants of this function:
var deps;
var defaultResult;
if (typeof observableFactory === 'function') {
deps = arg2 || [];
defaultResult = arg3;
}
else {
deps = [];
defaultResult = arg2;
}
// Create a ref that keeps the state we need
var monitor = React__default["default"].useRef({
hasResult: false,
result: defaultResult,
error: null,
});
// We control when component should rerender. Make triggerUpdate
// as examplified on React's docs at:
// https://reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate
var _a = React__default["default"].useReducer(function (x) { return x + 1; }, 0); _a[0]; var triggerUpdate = _a[1];
// Memoize the observable based on deps
var observable = React__default["default"].useMemo(function () {
// Make it remember previous subscription's default value when
// resubscribing.
var observable = typeof observableFactory === 'function'
? observableFactory()
: observableFactory;
if (!observable || typeof observable.subscribe !== 'function') {
if (observableFactory === observable) {
throw new TypeError("Given argument to useObservable() was neither a valid observable nor a function.");
}
else {
throw new TypeError("Observable factory given to useObservable() did not return a valid observable.");
}
}
if (!monitor.current.hasResult &&
typeof window !== 'undefined' // Don't do this in SSR
) {
// Optimize for BehaviorSubject and other observables implementing getValue():
if (typeof observable.hasValue !== 'function' || observable.hasValue()) {
if (typeof observable.getValue === 'function') {
monitor.current.result = observable.getValue();
monitor.current.hasResult = true;
}
else {
// Find out if the observable has a current value: try get it by subscribing and
// unsubscribing synchronously
var subscription = observable.subscribe(function (val) {
monitor.current.result = val;
monitor.current.hasResult = true;
});
// Unsubscribe directly. We only needed any synchronous value if it was possible.
if (typeof subscription === 'function') {
subscription();
}
else {
subscription.unsubscribe();
}
}
}
}
return observable;
}, deps);
// Integrate with react devtools:
React__default["default"].useDebugValue(monitor.current.result);
// Subscribe to the observable
React__default["default"].useEffect(function () {
var subscription = observable.subscribe(function (val) {
var current = monitor.current;
if (current.error !== null || current.result !== val) {
current.error = null;
current.result = val;
current.hasResult = true;
triggerUpdate();
}
}, function (err) {
var current = monitor.current;
if (current.error !== err) {
current.error = err;
triggerUpdate();
}
});
return typeof subscription === 'function'
? subscription // Support observables that return unsubscribe directly
: subscription.unsubscribe.bind(subscription);
}, deps);
// Throw if observable has emitted error so that
// an ErrorBoundrary can catch it
if (monitor.current.error)
throw monitor.current.error;
// Return the current result
return monitor.current.result;
}
function useLiveQuery(querier, deps, defaultResult) {
return useObservable(function () { return dexie.liveQuery(querier); }, deps || [], defaultResult);
}
function usePermissions(firstArg, table, obj) {
if (!firstArg)
throw new TypeError("Invalid arguments to usePermissions(): undefined or null");
var db;
if (arguments.length >= 3) {
if (!('transaction' in firstArg)) {
// Using ducktyping instead of instanceof in case there are multiple Dexie modules in app.
// First arg is ensures first arg is a Dexie instance
throw new TypeError("Invalid arguments to usePermission(db, table, obj): 1st arg must be a Dexie instance");
}
if (typeof table !== 'string')
throw new TypeError("Invalid arguments to usePermission(db, table, obj): 2nd arg must be string");
if (!obj || typeof obj !== 'object')
throw new TypeError("Invalid arguments to usePermission(db, table, obj): 3rd arg must be an object");
db = firstArg;
}
else {
if (firstArg instanceof dexie.Dexie)
throw new TypeError("Invalid arguments to usePermission(db, table, obj): Missing table and obj arguments.");
if (typeof firstArg.table === 'function' &&
typeof firstArg.db === 'object') {
db = firstArg.db;
obj = firstArg;
table = firstArg.table();
}
else {
throw new TypeError("Invalid arguments to usePermissions(). " +
"Expected usePermissions(entity: DexieCloudEntity) or " +
"usePermissions(db: Dexie, table: string, obj: DexieCloudObject)");
}
}
if (!('cloud' in db))
throw new Error("usePermissions() is only for Dexie Cloud but there's no dexie-cloud-addon active in given db.");
if (!('permissions' in db.cloud))
throw new Error("usePermissions() requires a newer version of dexie-cloud-addon. Please upgrade it.");
return useObservable(
// @ts-ignore
function () { return db.cloud.permissions(obj, table); }, [obj.realmId, obj.owner, table]);
}
exports.useLiveQuery = useLiveQuery;
exports.useObservable = useObservable;
exports.usePermissions = usePermissions;
Object.defineProperty(exports, '__esModule', { value: true });
}));
//# sourceMappingURL=dexie-react-hooks.js.map