use-mutable-source
Version:
Minimal and elegant way to integrate any library with React
519 lines (493 loc) • 17.5 kB
JavaScript
/**
* use-mutable-source v0.2.0
* https://paol-imi.github.io/use-mutable-source
* Copyright (c) 2022-present, Paolo Longo
* https://github.com/paol-imi/use-mutable-source/blob/main/LICENSE
* @license MIT
*/
;
Object.defineProperty(exports, '__esModule', { value: true });
var React = require('react');
var shim = require('use-sync-external-store/shim');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
function is(x, y) {
return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y;
}
function isRendering() {
try {
return React__default["default"].__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner.current !== null;
} catch (err) {
return false;
}
}
const warnings = /* @__PURE__ */ new Set();
function warning(message) {
if (!warnings.has(message)) {
warnings.add(message);
console.error(`[use-mutable-source]: ${message}`);
}
}
function useFactory(init, deps) {
const [ref, lazyInit] = React.useMemo(
() => [
{ current: null },
() => {
if (process.env.NODE_ENV !== "production") {
if (isRendering()) {
warning(
`It looks like you are attempting to read a source during render. Since the source would be lazily instantiated, causing side-effects, it cannot be used on render.
If the initialization of the source is side-effects free please use "usePureSource" instead.`
);
}
}
if (ref.current === null) {
[ref.current, ref.destroy] = init();
}
return ref.current;
}
],
deps
);
React.useEffect(() => {
lazyInit();
return () => {
var _a;
(_a = ref.destroy) == null ? void 0 : _a.call(ref, ref.current);
ref.current = null;
};
}, [ref, lazyInit]);
return [ref, lazyInit];
}
const usePureFactory = React.useMemo;
function useSnapshot$3(getSnapshot, subscribe) {
const getMemoizedSnapshot = React.useMemo(() => {
let snapshot = null;
return () => {
snapshot = getSnapshot(snapshot);
return snapshot;
};
}, [getSnapshot]);
if (process.env.NODE_ENV !== "production") {
if (!is(getMemoizedSnapshot(), getMemoizedSnapshot())) {
warning(
`"getSnapshot" must be a pure function, and if called multiple times in a row, it must return the same snapshot.
This is necessary to avoid an infinite render loop.`
);
}
}
return shim.useSyncExternalStore(
subscribe,
getMemoizedSnapshot,
getMemoizedSnapshot
);
}
function useSource$b(init, deps = []) {
const [ref, lazyInit] = useFactory(init, deps);
const useSourceSnapshot = React.useCallback(
function useSourceSnapshot2(getSnapshot, ...rest) {
const [getSnapshotDeps = [], subscribe, subscribeDeps = []] = typeof rest[0] === "function" ? [void 0, rest[0], rest[1]] : [rest[0], rest[1], rest[2]];
const memoizedSubscribe = React.useCallback(
(onChange) => {
if (process.env.NODE_ENV !== "production") {
const fallback = getSnapshot(null, null);
const snapshot2 = getSnapshot(lazyInit(), fallback);
if (!is(fallback, snapshot2)) {
warning(
`The initial snapshot (derived when the source was not yet created) is different from the snapshot derived from the source. This will force a new render and may hurt performance.
Initial snapshot: ${JSON.stringify(fallback, null, 2)}
Actual snapshot: ${JSON.stringify(snapshot2, null, 2)}`
);
}
}
return subscribe(lazyInit(), onChange);
},
[lazyInit, ...subscribeDeps]
);
const getSourceSnapshot = React.useCallback(
(currentSnapshot) => getSnapshot(ref.current, currentSnapshot),
[ref, ...getSnapshotDeps]
);
const snapshot = useSnapshot$3(getSourceSnapshot, memoizedSubscribe);
if (process.env.NODE_ENV !== "production") {
React.useDebugValue(snapshot);
}
return snapshot;
},
[ref, lazyInit]
);
if (process.env.NODE_ENV !== "production") {
React.useDebugValue(ref);
}
return [useSourceSnapshot, lazyInit];
}
function usePureSource$b(init, deps = []) {
const source = usePureFactory(init, deps);
const useSourceSnapshot = React.useCallback(
function useSourceSnapshot2(getSnapshot, ...rest) {
const [getSnapshotDeps = [], subscribe, subscribeDeps = []] = typeof rest[0] === "function" ? [void 0, rest[0], rest[1]] : [rest[0], rest[1], rest[2]];
const memoizedSubscribe = React.useCallback(
(onChange) => subscribe(source, onChange),
[source, ...subscribeDeps]
);
const getSourceSnapshot = React.useCallback(
(currentSnapshot) => getSnapshot(source, currentSnapshot),
[source, ...getSnapshotDeps]
);
const snapshot = useSnapshot$3(getSourceSnapshot, memoizedSubscribe);
if (process.env.NODE_ENV !== "production") {
React.useDebugValue(snapshot);
}
return snapshot;
},
[source]
);
if (process.env.NODE_ENV !== "production") {
React.useDebugValue(source);
}
return [useSourceSnapshot, source];
}
const useSource$a = () => {
return [
(getSnapshot, ...rest) => getSnapshot(null, null),
() => null
];
};
const usePureSource$a = (init) => {
const source = init();
return [
(getSnapshot, ...rest) => getSnapshot(source, null),
source
];
};
const isServer = typeof window === "undefined" || typeof window.document === "undefined" || typeof window.document.createElement === "undefined";
const [useSource$9, usePureSource$9] = !isServer ? [useSource$b, usePureSource$b] : [useSource$a, usePureSource$a];
function useSnapshot$2(getSnapshot, subscribe) {
const [snapshot, setSnapshot] = React.useState(() => getSnapshot(null));
getSnapshot = React.useCallback(getSnapshot, []);
React.useLayoutEffect(
() => {
if (!is(snapshot, getSnapshot(snapshot)))
setSnapshot(getSnapshot);
return subscribe(() => setSnapshot(getSnapshot));
},
[subscribe]
);
return snapshot;
}
function useSource$8(init, getSnapshot, subscribe, subscribeDeps = []) {
const [ref, lazyInit] = useFactory(init, []);
const memoizedSubscribe = React.useCallback(
(onChange) => {
if (process.env.NODE_ENV !== "production") {
const fallback = getSnapshot(null, null);
const snapshot2 = getSnapshot(lazyInit(), fallback);
if (!is(fallback, snapshot2)) {
warning(
`The initial snapshot (derived when the source was not yet created) is different from the snapshot derived from the source. This will force a new render and may hurt performance.
Initial snapshot: ${JSON.stringify(fallback, null, 2)}
Actual snapshot: ${JSON.stringify(snapshot2, null, 2)}`
);
}
}
return subscribe(lazyInit(), onChange);
},
subscribeDeps
);
const snapshot = useSnapshot$2(
(currentSnapshot) => getSnapshot(ref.current, currentSnapshot),
memoizedSubscribe
);
if (process.env.NODE_ENV !== "production") {
React.useDebugValue([snapshot, ref]);
}
return [snapshot, lazyInit];
}
function usePureSource$8(init, getSnapshot, subscribe, subscribeDeps = []) {
const source = usePureFactory(init, []);
const memoizedSubscribe = React.useCallback(
(onChange) => subscribe(source, onChange),
subscribeDeps
);
const snapshot = useSnapshot$2(
(currentSnapshot) => getSnapshot(source, currentSnapshot),
memoizedSubscribe
);
if (process.env.NODE_ENV !== "production") {
React.useDebugValue([snapshot, source]);
}
return [snapshot, source];
}
const useSource$7 = (_, getSnapshot) => {
return [getSnapshot(null, null), () => null];
};
const usePureSource$7 = (init, getSnapshot) => {
const source = init();
return [getSnapshot(source, null), source];
};
const [useSource$6, usePureSource$6] = !isServer ? [useSource$8, usePureSource$8] : [useSource$7, usePureSource$7];
class Slice {
constructor() {
this.version = 0;
this.listeners = /* @__PURE__ */ new Set();
this.update = () => {
this.version++;
this.listeners.forEach((listener) => listener());
};
this.subscribe = (listener) => {
this.listeners.add(listener);
return () => void this.listeners.delete(listener);
};
}
}
function useSnapshot$1(getSnapshot, slice) {
const subscribe = React.useCallback(
(onChange) => slice.subscribe(onChange),
[slice]
);
const ref = React.useMemo(
() => ({
lastReadVersion: slice.version,
snapshot: getSnapshot(null),
getter: () => {
if (ref.lastReadVersion !== slice.version) {
ref.snapshot = getSnapshot(ref.snapshot);
ref.lastReadVersion = slice.version;
}
return ref.snapshot;
}
}),
[getSnapshot, slice]
);
return shim.useSyncExternalStore(subscribe, ref.getter, ref.getter);
}
function useSource$5(init, ...rest) {
const [deps = [], contract] = rest.length === 1 ? [[], rest[0]] : rest;
const slice = React.useMemo(() => new Slice(), deps);
const [ref, lazyInit] = useFactory(() => {
const [source, destroy] = init();
const unsubscribe = contract(source, slice.update);
return [
source,
() => {
unsubscribe == null ? void 0 : unsubscribe();
destroy == null ? void 0 : destroy(source);
}
];
}, deps);
const useSourceSnapshot = React.useCallback(
function useSourceSnapshot2(getSnapshot, getSnapshotDeps = []) {
const getSourceSnapshot = React.useCallback(
(currentSnapshot) => getSnapshot(ref.current, currentSnapshot),
[ref, ...getSnapshotDeps]
);
if (process.env.NODE_ENV !== "production") {
React.useEffect(() => {
const fallback = getSnapshot(null, null);
const snapshot2 = getSnapshot(lazyInit(), fallback);
if (!is(fallback, snapshot2)) {
warning(
`The initial snapshot (derived when the source was not yet created) is different from the snapshot derived from the source. This will force a new render and may hurt performance.
Initial snapshot: ${JSON.stringify(fallback, null, 2)}
Actual snapshot: ${JSON.stringify(snapshot2, null, 2)}`
);
}
}, []);
}
const snapshot = useSnapshot$1(getSourceSnapshot, slice);
if (process.env.NODE_ENV !== "production") {
React.useDebugValue(snapshot);
}
return snapshot;
},
[lazyInit, ref, slice]
);
if (process.env.NODE_ENV !== "production") {
React.useDebugValue(ref);
}
return [useSourceSnapshot, lazyInit];
}
function usePureSource$5(init, ...rest) {
const [deps = [], contract] = rest.length === 1 ? [[], rest[0]] : rest;
const slice = React.useMemo(() => new Slice(), deps);
const source = usePureFactory(() => {
const source2 = init();
contract(source2, slice.update);
return source2;
}, deps);
const useSourceSnapshot = React.useCallback(
function useSourceSnapshot2(getSnapshot, getSnapshotDeps = []) {
const getSourceSnapshot = React.useCallback(
(currentSnapshot) => getSnapshot(source, currentSnapshot),
[source, ...getSnapshotDeps]
);
const snapshot = useSnapshot$1(getSourceSnapshot, slice);
if (process.env.NODE_ENV !== "production") {
React.useDebugValue(snapshot);
}
return snapshot;
},
[source, slice]
);
if (process.env.NODE_ENV !== "production") {
React.useDebugValue(source);
}
return [useSourceSnapshot, source];
}
function withContract$2(source, contract) {
const slice = new Slice();
contract(source, slice.update);
function useSourceSnapshot(getSnapshot, getSnapshotDeps = []) {
const getSourceSnapshot = React.useCallback(
(currentSnapshot) => getSnapshot(source, currentSnapshot),
getSnapshotDeps
);
const snapshot = useSnapshot$1(getSourceSnapshot, slice);
if (process.env.NODE_ENV !== "production") {
React.useDebugValue(snapshot);
}
return snapshot;
}
return [useSourceSnapshot, source, slice];
}
const useSource$4 = () => {
return [
(getSnapshot) => getSnapshot(null, null),
() => null
];
};
const usePureSource$4 = (init, ...rest) => {
const source = init();
return [
(getSnapshot) => getSnapshot(source, null),
source
];
};
const withContract$1 = (source) => {
return [
(getSnapshot) => getSnapshot(source, null),
source,
null
];
};
const [useSource$3, usePureSource$3, withContract] = !isServer ? [useSource$5, usePureSource$5, withContract$2] : [useSource$4, usePureSource$4, withContract$1];
function useSnapshot(getSnapshot, slice) {
const [snapshot, setSnapshot] = React.useState(() => getSnapshot(null));
const initialVersion = slice.version;
getSnapshot = React.useCallback(getSnapshot, []);
React.useLayoutEffect(() => {
if (initialVersion !== slice.version) {
setSnapshot(getSnapshot);
}
return slice.subscribe(() => setSnapshot(getSnapshot));
}, [slice]);
return snapshot;
}
function useSource$2(init, contract, getSnapshot) {
const slice = React.useMemo(() => new Slice(), []);
const [ref, lazyInit] = useFactory(() => {
const [source, destroy] = init();
const unsubscribe = contract(source, slice.update);
return [
source,
() => {
unsubscribe == null ? void 0 : unsubscribe();
destroy == null ? void 0 : destroy(source);
}
];
}, []);
const snapshot = useSnapshot(
(currentSnapshot) => getSnapshot(ref.current, currentSnapshot),
slice
);
if (process.env.NODE_ENV !== "production") {
React.useEffect(() => {
const fallback = getSnapshot(null, null);
const snapshot2 = getSnapshot(lazyInit(), fallback);
if (!is(fallback, snapshot2)) {
warning(
`The initial snapshot (derived when the source was not yet created) is different from the snapshot derived from the source. This will force a new render and may hurt performance.
Initial snapshot: ${JSON.stringify(fallback, null, 2)}
Actual snapshot: ${JSON.stringify(snapshot2, null, 2)}`
);
}
}, []);
}
if (process.env.NODE_ENV !== "production") {
React.useDebugValue([snapshot, ref]);
}
return [snapshot, lazyInit];
}
function usePureSource$2(init, contract, getSnapshot) {
const slice = React.useMemo(() => new Slice(), []);
const source = usePureFactory(() => {
const source2 = init();
contract(source2, slice.update);
return source2;
}, []);
const snapshot = useSnapshot(
(currentSnapshot) => getSnapshot(source, currentSnapshot),
slice
);
if (process.env.NODE_ENV !== "production") {
React.useDebugValue([snapshot, source]);
}
return [snapshot, source];
}
const useSource$1 = (init, contract, getSnapshot) => {
return [getSnapshot(null, null), () => null];
};
const usePureSource$1 = (init, contract, getSnapshot) => {
const source = init();
return [getSnapshot(source, null), source];
};
const [useSource, usePureSource] = !isServer ? [useSource$2, usePureSource$2] : [useSource$1, usePureSource$1];
function shallowEqual(objA, objB) {
if (is(objA, objB)) {
return true;
}
if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) {
return false;
}
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
for (let i = 0; i < keysA.length; i++) {
if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
return false;
}
}
return true;
}
exports.shallowEqual = shallowEqual;
exports.useAtomicPureSourceWithContract = usePureSource;
exports.useAtomicPureSourceWithContractClient = usePureSource$2;
exports.useAtomicPureSourceWithContractServer = usePureSource$1;
exports.useAtomicPureSourceWithSubscription = usePureSource$6;
exports.useAtomicPureSourceWithSubscriptionClient = usePureSource$8;
exports.useAtomicPureSourceWithSubscriptionServer = usePureSource$7;
exports.useAtomicSourceWithContract = useSource;
exports.useAtomicSourceWithContractClient = useSource$2;
exports.useAtomicSourceWithContractServer = useSource$1;
exports.useAtomicSourceWithSubscription = useSource$6;
exports.useAtomicSourceWithSubscriptionClient = useSource$8;
exports.useAtomicSourceWithSubscriptionServer = useSource$7;
exports.useFactory = useFactory;
exports.usePureFactory = usePureFactory;
exports.usePureSourceWithContract = usePureSource$3;
exports.usePureSourceWithContractClient = usePureSource$5;
exports.usePureSourceWithContractServer = usePureSource$4;
exports.usePureSourceWithSubscription = usePureSource$9;
exports.usePureSourceWithSubscriptionClient = usePureSource$b;
exports.usePureSourceWithSubscriptionServer = usePureSource$a;
exports.useSourceWithContract = useSource$3;
exports.useSourceWithContractClient = useSource$5;
exports.useSourceWithContractServer = useSource$4;
exports.useSourceWithSubscription = useSource$9;
exports.useSourceWithSubscriptionClient = useSource$b;
exports.useSourceWithSubscriptionServer = useSource$a;
exports.withContract = withContract;
exports.withContractClient = withContract$2;
exports.withContractServer = withContract$1;