Skip to content

Commit

Permalink
feat(atom): add updateAsTransaction(), update Transacted
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Mar 30, 2023
1 parent 3b39d61 commit 113852c
Showing 1 changed file with 53 additions and 0 deletions.
53 changes: 53 additions & 0 deletions packages/atom/src/transacted.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {
DeepPath,
Fn,
OptPathVal,
Path,
Path0,
Expand Down Expand Up @@ -37,6 +38,31 @@ export const defTransacted = <T>(parent: IAtom<T>) => new Transacted(parent);
export const beginTransaction = <T>(parent: IAtom<T>) =>
new Transacted(parent).begin();

/**
* An "anonymous" version of {@link Transacted.updateAsTransaction}. Takes an
* atom, wraps it as a {@link Transacted} and calls given `fn` with it to update
* the state as a single transaction. If the update function returns true, the
* transaction will be committed, else cancelled. Returns atom.
*
* @remarks
* **IMPORTANT:** Within body of the update function **only** work with the
* transaction wrapper given as argument! **DO NOT** update the original state
* atom!
*
* If an error occurs during the update, the transaction will be canceled, the
* wrapper silently removed and the error re-thrown.
*
* @param parent
* @param fn
*/
export const updateAsTransaction = <T>(
parent: IAtom<T>,
fn: Fn<Transacted<T>, boolean>
) => {
new Transacted(parent).updateAsTransaction(fn);
return parent;
};

export class Transacted<T> implements IAtom<T> {
parent: IAtom<T>;
current: T | undefined;
Expand Down Expand Up @@ -208,6 +234,33 @@ export class Transacted<T> implements IAtom<T> {
this.isActive = false;
}

/**
* Starts a new transaction and calls given `fn` with this instance to
* update the state (presumably in multiple stages) as a single transaction.
* If the update function returns true, the transaction will be committed,
* else cancelled.
*
* @remarks
* **IMPORTANT:** Within body of the update function **only** work with the
* transaction wrapper given as argument! **DO NOT** update the original
* state atom!
*
* If an error occurs during the update, the transaction will be canceled
* and the error re-thrown.
*
* @param parent
* @param fn
*/
updateAsTransaction(fn: Fn<Transacted<T>, boolean>) {
try {
this.begin();
fn(this) ? this.commit() : this.cancel();
} catch (e) {
this.cancel();
throw e;
}
}

addWatch(id: string, watch: Watch<T>) {
return this.parent.addWatch(this.id + id, (_, prev, curr) =>
watch(id, prev, curr)
Expand Down

0 comments on commit 113852c

Please sign in to comment.