UNPKG

jotai-effect

Version:
224 lines 8.87 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.atomEffect = atomEffect; const vanilla_1 = require("jotai/vanilla"); const internals_1 = require("jotai/vanilla/internals"); const env_1 = require("./env.js"); function getBuildingBlocks(store) { const buildingBlocks = (0, internals_1.INTERNAL_getBuildingBlocksRev1)(store); return [ buildingBlocks[1], // mountedAtoms buildingBlocks[3], // changedAtoms (0, internals_1.INTERNAL_initializeStoreHooks)(buildingBlocks[6]), // storeHooks buildingBlocks[11], // ensureAtomState buildingBlocks[14], // readAtomState buildingBlocks[16], // writeAtomState buildingBlocks[17], // mountDependencies buildingBlocks[15], // invalidateDependents buildingBlocks[13], // recomputeInvalidatedAtoms buildingBlocks[12], // flushCallbacks ]; } function atomEffect(effect) { const refAtom = (0, vanilla_1.atom)(() => []); const effectAtom = (0, vanilla_1.atom)(function effectAtomRead(get) { const [dependencies, atomState, mountedAtoms] = get(refAtom); if (mountedAtoms.has(effectAtom)) { dependencies.forEach(get); ++atomState.n; } }); effectAtom.effect = effect; effectAtom.unstable_onInit = (store) => { const deps = new Set(); let inProgress = 0; let isRecursing = false; let hasChanged = false; let fromCleanup = false; let runCleanup; function runEffect() { if (inProgress) { return; } deps.clear(); let isSync = true; const getter = (a) => { var _a; if (fromCleanup) { return store.get(a); } if ((0, internals_1.INTERNAL_isSelfAtom)(effectAtom, a)) { const aState = ensureAtomState(a); if (!(0, internals_1.INTERNAL_isAtomStateInitialized)(aState)) { if ((0, internals_1.INTERNAL_hasInitialValue)(a)) { (0, internals_1.INTERNAL_setAtomStateValueOrPromise)(a, a.init, ensureAtomState); } else { // NOTE invalid derived atoms can reach here throw new Error('no atom init'); } } return (0, internals_1.INTERNAL_returnAtomValue)(aState); } // a !== atom const aState = readAtomState(a); try { return (0, internals_1.INTERNAL_returnAtomValue)(aState); } finally { atomState.d.set(a, aState.n); (_a = mountedAtoms.get(a)) === null || _a === void 0 ? void 0 : _a.t.add(effectAtom); if (isSync) { deps.add(a); } else { if (mountedAtoms.has(a)) { mountDependencies(effectAtom); recomputeInvalidatedAtoms(); flushCallbacks(); } } } }; getter.peek = store.get; const setter = (a, ...args) => { var _a; const aState = ensureAtomState(a); try { ++inProgress; if ((0, internals_1.INTERNAL_isSelfAtom)(effectAtom, a)) { if (!(0, internals_1.INTERNAL_hasInitialValue)(a)) { // NOTE technically possible but restricted as it may cause bugs throw new Error('atom not writable'); } const prevEpochNumber = aState.n; const v = args[0]; (0, internals_1.INTERNAL_setAtomStateValueOrPromise)(a, v, ensureAtomState); mountDependencies(a); if (prevEpochNumber !== aState.n) { changedAtoms.add(a); (_a = storeHooks.c) === null || _a === void 0 ? void 0 : _a.call(storeHooks, a); invalidateDependents(a); } return undefined; } else { return writeAtomState(a, ...args); } } finally { if (!isSync) { recomputeInvalidatedAtoms(); flushCallbacks(); } --inProgress; } }; setter.recurse = (a, ...args) => { if (fromCleanup) { if ((0, env_1.isDev)()) { throw new Error('set.recurse is not allowed in cleanup'); } return undefined; } try { isRecursing = true; mountDependencies(effectAtom); return setter(a, ...args); } finally { recomputeInvalidatedAtoms(); isRecursing = false; if (hasChanged) { hasChanged = false; runEffect(); } } }; try { runCleanup === null || runCleanup === void 0 ? void 0 : runCleanup(); const cleanup = effectAtom.effect(getter, setter); if (typeof cleanup !== 'function') { return; } runCleanup = () => { if (inProgress) { return; } try { isSync = true; fromCleanup = true; return cleanup(); } finally { isSync = false; fromCleanup = false; runCleanup = undefined; } }; } finally { isSync = false; deps.forEach((depAtom) => { atomState.d.set(depAtom, ensureAtomState(depAtom).n); }); mountDependencies(effectAtom); recomputeInvalidatedAtoms(); } } const [mountedAtoms, changedAtoms, storeHooks, ensureAtomState, readAtomState, writeAtomState, mountDependencies, invalidateDependents, recomputeInvalidatedAtoms, flushCallbacks,] = getBuildingBlocks(store); const atomEffectChannel = ensureAtomEffectChannel(store); const atomState = ensureAtomState(effectAtom); // initialize atomState atomState.v = undefined; Object.assign(store.get(refAtom), [deps, atomState, mountedAtoms]); storeHooks.m.add(effectAtom, function atomOnMount() { // mounted atomEffectChannel.add(runEffect); if (runCleanup) { atomEffectChannel.delete(runCleanup); } }); storeHooks.u.add(effectAtom, function atomOnUnmount() { // unmounted atomEffectChannel.delete(runEffect); if (runCleanup) { atomEffectChannel.add(runCleanup); } }); storeHooks.c.add(effectAtom, function atomOnUpdate() { // changed if (isRecursing) { hasChanged = true; } else { atomEffectChannel.add(runEffect); } }); }; if ((0, env_1.isDev)()) { Object.defineProperty(refAtom, 'debugLabel', { get: () => effectAtom.debugLabel ? `${effectAtom.debugLabel}:ref` : undefined, }); refAtom.debugPrivate = true; } return effectAtom; } const atomEffectChannelStoreMap = new WeakMap(); function ensureAtomEffectChannel(store) { const storeHooks = getBuildingBlocks(store)[2]; let atomEffectChannel = atomEffectChannelStoreMap.get(store); if (!atomEffectChannel) { atomEffectChannel = new Set(); atomEffectChannelStoreMap.set(store, atomEffectChannel); storeHooks.f.add(function storeOnFlush() { // flush for (const fn of atomEffectChannel) { atomEffectChannel.delete(fn); fn(); } }); } return atomEffectChannel; } //# sourceMappingURL=atomEffect.js.map