92 lines
2.4 KiB
TypeScript
92 lines
2.4 KiB
TypeScript
import { Queue } from 'async-await-queue';
|
|
import type { IReactionDisposer, IReactionOptions, IReactionPublic } from 'mobx';
|
|
import { reaction } from 'mobx';
|
|
import { debounce, omit } from 'radash';
|
|
|
|
export function disposableReaction<T, FireImmediately extends boolean = false>(
|
|
disposeExpression: () => boolean,
|
|
expression: (r: IReactionPublic) => T,
|
|
effect: (
|
|
arg: T,
|
|
prev: FireImmediately extends true ? T | undefined : T,
|
|
r: IReactionPublic
|
|
) => void,
|
|
reactionOpts?: IReactionOptions<T, FireImmediately>
|
|
) {
|
|
let disposer: IReactionDisposer | undefined;
|
|
|
|
if (!disposeExpression()) {
|
|
disposer = reaction(expression, effect, reactionOpts);
|
|
}
|
|
|
|
function cleanDisposer() {
|
|
disposer = undefined;
|
|
}
|
|
|
|
reaction(disposeExpression, (mustBeDisposed) => {
|
|
if (mustBeDisposed) {
|
|
if (disposer !== undefined) disposer();
|
|
cleanDisposer();
|
|
} else {
|
|
setTimeout(() => {
|
|
disposer = reaction(
|
|
expression,
|
|
effect,
|
|
reactionOpts ? omit(reactionOpts, ['fireImmediately']) : undefined
|
|
);
|
|
}, 100);
|
|
}
|
|
});
|
|
}
|
|
|
|
export function debouncedReaction<T, FireImmediately extends boolean = false>(
|
|
expression: (r: IReactionPublic) => T,
|
|
effect: (
|
|
arg: T,
|
|
prev: FireImmediately extends true ? T | undefined : T,
|
|
r: IReactionPublic
|
|
) => void,
|
|
{ wait, ...reactionOpts }: IReactionOptions<T, FireImmediately> & { wait: number }
|
|
): IReactionDisposer {
|
|
const debouncedEffect = debounce({ delay: wait }, effect);
|
|
|
|
return reaction(expression, debouncedEffect, reactionOpts);
|
|
}
|
|
|
|
const queue = new Queue(1, 10);
|
|
|
|
export function queueReaction<T, FireImmediately extends boolean = false>(
|
|
expression: (r: IReactionPublic) => T,
|
|
effect: (
|
|
arg: T,
|
|
prev: FireImmediately extends true ? T | undefined : T,
|
|
r: IReactionPublic
|
|
) => Promise<void>,
|
|
{
|
|
priority,
|
|
name,
|
|
...reactionOpts
|
|
}: IReactionOptions<T, FireImmediately> & { name: string; priority: number }
|
|
): IReactionDisposer {
|
|
const me = Symbol(name);
|
|
|
|
async function queueEffect(
|
|
arg: T,
|
|
prev: FireImmediately extends true ? T | undefined : T,
|
|
r: IReactionPublic
|
|
) {
|
|
await queue.wait(me, priority);
|
|
|
|
try {
|
|
await effect(arg, prev, r);
|
|
} catch (error) {
|
|
// eslint-disable-next-line no-console
|
|
console.error(error);
|
|
} finally {
|
|
queue.end(me);
|
|
}
|
|
}
|
|
|
|
return reaction(expression, queueEffect, reactionOpts);
|
|
}
|