@2toad/reflex
Version:
A simple approach to state management
124 lines • 4.31 kB
JavaScript
;
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