UNPKG

@preact-signals/safe-react

Version:
144 lines (141 loc) 4.08 kB
"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