preactjs / signals

Manage state with style in every framework
https://preactjs.com/blog/introducing-signals/
MIT License
3.71k stars 91 forks source link

Allow effect code to end the effect? #395

Open pjeby opened 1 year ago

pjeby commented 1 year ago

Currently, effects can only be ended from the "outside" - i.e., the caller creating the effect. But there are times when it's advantageous to allow an effect to cancel itself, since it knows when it is "done". (In particular, one can have effects decide when to end based on a shared signal, which is the use case I'm working on right now.)

In principle, you can share the dispose callback from the enclosing scope, and perhaps even make a helper function to simplify that process. But since the effect callback is synchronously invoked, this creates an edge case where you can't dispose the effect before the effect() call returns. Basically, you currently need a helper like this:

function disposableEffect(fn: (dispose: () => void) => void | (() => void)) {
    let dispose = function () {
        // wait for the effect() call to return
       Promise.resolve().then(() => dispose());
    }
    return dispose = effect(function(){
        return fn.call(this, dispose);
    });
}

This seems like it would be greatly simplified by making what is now _dispose into a public method, so that effect callbacks can just call this.dispose(), or even just return a special value to indicate that they wish to be disposed of.

(Note: while you can certainly stop an effect from having dependencies eventually, you can't actually get it to release without first receiving another callback, and if the reason you're trying to end the effect is because there aren't going to be any more callbacks, simply dropping all dependencies doesn't work to dispose of the reference cycle between the effect and its final dependencies.)