UNPKG

@2toad/reflex

Version:

A simple approach to state management

124 lines 4.31 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.computed = computed; const types_1 = require("./types"); const reflex_1 = require("./reflex"); /** * Creates a computed reactive value that depends on other reactive values. * The computed value automatically updates when any of its dependencies change. * * @param dependencies - Array of reflex values this computation depends on * @param compute - Function that computes the result from the current dependency values * @returns A read-only reflex value that updates automatically when dependencies change */ function computed(dependencies, compute) { const state = { isComputing: false, isAsync: false, currentComputed: undefined, computePromise: null, cleanups: [], subscriptionCount: 0, }; const result = (0, reflex_1.reflex)({ initialValue: state.currentComputed }); const recompute = async () => { if (state.isComputing) { await state.computePromise; return; } state.isComputing = true; try { const depValues = dependencies.map((dep) => dep.value); if (depValues.some((v) => v === undefined)) { state.currentComputed = undefined; result.setValue(state.currentComputed); return; } const computeResult = compute(depValues); if (computeResult instanceof Promise) { state.isAsync = true; try { const value = await computeResult; if (state.subscriptionCount > 0) { state.currentComputed = value; result.setValue(value); } } catch (error) { console.error("[Computed Error] Async compute function failed:", error); } finally { state.isComputing = false; state.computePromise = null; } } else { state.currentComputed = computeResult; result.setValue(computeResult); } } catch (error) { console.error("[Computed Error] Compute function failed:", error); } finally { state.isComputing = false; } }; const setupDependencies = () => { state.cleanups.forEach((cleanup) => cleanup()); state.cleanups = []; state.cleanups = dependencies.map((dep) => dep.subscribe(() => { if (state.subscriptionCount > 0) { recompute(); } })); recompute(); }; const cleanup = () => { state.cleanups.forEach((cleanup) => cleanup()); state.cleanups = []; }; const originalSubscribe = result.subscribe; return { get value() { return state.currentComputed; }, setValue() { throw new Error(types_1.COMPUTED_ERRORS.SET_VALUE); }, setValueAsync() { throw new Error(types_1.COMPUTED_ERRORS.SET_VALUE); }, subscribe(callback) { if (state.subscriptionCount === 0) { setupDependencies(); } state.subscriptionCount++; const unsubscribe = originalSubscribe.call(result, callback); return () => { unsubscribe(); state.subscriptionCount--; if (state.subscriptionCount === 0) { cleanup(); } }; }, batch(updateFn) { if (state.isAsync) { throw new Error(types_1.COMPUTED_ERRORS.SYNC_BATCH); } return updateFn(state.currentComputed); }, async batchAsync(updateFn) { await state.computePromise; return Promise.resolve(updateFn(state.currentComputed)); }, addMiddleware() { throw new Error(types_1.COMPUTED_ERRORS.ADD_MIDDLEWARE); }, removeMiddleware() { throw new Error(types_1.COMPUTED_ERRORS.REMOVE_MIDDLEWARE); }, }; } //# sourceMappingURL=computed.js.map