@preact-signals/safe-react
Version:
Manage state with style in React
144 lines (141 loc) • 4.08 kB
JavaScript
"use client";
import { Signal, effect } from '@preact/signals-core';
import { version, useRef } from 'react';
import { useSyncExternalStore } from 'use-sync-external-store/shim/index.js';
const ReactElemType = Symbol.for(
parseInt(version) >= 19 ? "react.transitional.element" : "react.element"
);
const symDispose = Symbol.dispose || Symbol.for("Symbol.dispose");
const _queueMicrotask = Promise.prototype.then.bind(Promise.resolve());
const resetSyncRerendersSet = /* @__PURE__ */ new Set();
let isResetSyncRerendersScheduled = false;
const resetSyncRerenders = () => {
isResetSyncRerendersScheduled = false;
resetSyncRerendersSet.forEach((store) => {
store["r" /* resetSyncRerenders */]();
});
resetSyncRerendersSet.clear();
};
const scheduleResetSyncRerenders = (store) => {
if (!isResetSyncRerendersScheduled) {
isResetSyncRerendersScheduled = true;
void _queueMicrotask(resetSyncRerenders);
}
if (!resetSyncRerendersSet.has(store)) {
resetSyncRerendersSet.add(store);
}
};
let useSignalsDepth = 0;
let cleanUpFn = void 0;
const maxSyncRerenders = 25;
function createEffectStore() {
let effectInstance;
let version2 = 0;
let onChangeNotifyReact;
let unsubscribe = effect(function() {
effectInstance = this;
});
let inRender = false;
let syncRerendersCount = 0;
effectInstance["c" /* onDepsChange */] = function() {
if (inRender) {
return;
}
if (syncRerendersCount > maxSyncRerenders) {
throw new Error(
`preact-signals: Too many sync rerenders (${syncRerendersCount}), you might change parent component signal dependencies in render of child component.`
);
}
version2 = version2 + 1 | 0;
if (!onChangeNotifyReact) {
return;
}
onChangeNotifyReact();
};
return {
effect: effectInstance,
subscribe(onStoreChange) {
onChangeNotifyReact = onStoreChange;
return function() {
version2 = version2 + 1 | 0;
onChangeNotifyReact = void 0;
unsubscribe();
};
},
["r" /* resetSyncRerenders */]() {
syncRerendersCount = 0;
},
["s" /* startTracking */]() {
inRender = true;
syncRerendersCount++;
scheduleResetSyncRerenders(this);
if (!useSignalsDepth && cleanUpFn) {
throw new Error("cleanUpFn should be undefined");
}
if (useSignalsDepth && !cleanUpFn) {
throw new Error("cleanUpFn should be defined with depth");
}
if (!useSignalsDepth) {
cleanUpFn = effectInstance["S" /* startTracking */]();
}
useSignalsDepth++;
},
getSnapshot() {
return version2;
},
["f" /* finishTracking */]() {
if (useSignalsDepth < 1) {
throw new Error("useSignalsDepth should be non-negative");
}
try {
if (useSignalsDepth === 1 && !cleanUpFn) {
throw new Error("cleanUpFn should be defined with depth");
}
if (useSignalsDepth === 1 && cleanUpFn) {
try {
cleanUpFn();
} finally {
inRender = false;
cleanUpFn = void 0;
}
}
} finally {
useSignalsDepth--;
}
},
[symDispose]() {
this["f" /* finishTracking */]();
}
};
}
function useSignals() {
const storeRef = useRef();
if (storeRef.current == null) {
storeRef.current = createEffectStore();
}
const store = storeRef.current;
useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
store["s" /* startTracking */]();
return store;
}
function SignalValue(props) {
const effectStore = useSignals();
try {
return props.data.value;
} finally {
effectStore["f" /* finishTracking */]();
}
}
Object.defineProperties(Signal.prototype, {
$$typeof: { configurable: true, value: ReactElemType },
type: { configurable: true, value: SignalValue },
props: {
configurable: true,
get() {
return { data: this };
}
},
ref: { configurable: true, value: null }
});
export { useSignals };
//# sourceMappingURL=tracking.mjs.map