mobxjs / mobxjs/mobx-utils

Debounced computeds

Open
#283 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
1.2k
Forks
130
PR merge metrics
No merged PRs in 30d

Description

I've been playing with debouncing computeds. I have a complex & CPU-expensive computed that subscribes to many observables, and I'd to debounce recalculations there.

I haven't been able to find a nice way to do this. #24 gets close, but was never merged, and only works for debouncing downstream updates, not debouncing incoming updates, so isn't really sufficient (unless your computed has only one or two inputs).

I know this breaks the general contract of @computed, fully agree this shouldn't be a common case, but I do think it's a useful niche case. I've got the below currently working as drop-in alternative for @computed decorators, which I think works nicely.

A) Is there anything obviously wrong with this approach, or any other existing alternatives I've missed?
B) If not, would you be interested in including it in mobx-utils?

function debounceComputed<T>(timeoutMs: number, computedOptions: IComputedValueOptions<T> = {}) {
    return <T>(target: any, key: string, descriptor: PropertyDescriptor): void => {
        if (!descriptor.get) throw new Error('debounceComputed requires a getter');

        const internalFn = descriptor.get as () => T;
        let cachedValue: { value: T, atom: IAtom } | undefined;

        return computed(computedOptions)(target, key, { ...descriptor, get: function () {
            if (cachedValue) {
                // Don't calculate until the atom pings us
                cachedValue.atom.reportObserved();
            } else {
                // Calculate and cache the result:
                cachedValue = { value: internalFn.apply(this), atom: createAtom("DebounceAtom") };

                // Batch subsequent runs for the next timeoutMs:
                setTimeout(() => {
                    const { atom } = cachedValue!;
                    cachedValue = undefined;
                    atom.reportChanged(); // Ping subscribers to update
                }, timeoutMs);
            }
            return cachedValue.value;
        }});
    };
}

I'm still using mobx 5, but as far as I'm aware the same concept (same code?) should work just the same in v6.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

No repository files or tests are named. Start by reviewing the proposed debounceComputed decorator and the MobX 5 and 6 computed/createAtom behavior it relies on; done would require agreement on the API and semantics, followed by an accepted implementation and tests for debounced incoming updates.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
frontend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.