Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose .subsribe method? #15

Closed
dy opened this issue Nov 19, 2022 · 16 comments
Closed

Expose .subsribe method? #15

dy opened this issue Nov 19, 2022 · 16 comments

Comments

@dy
Copy link

dy commented Nov 19, 2022

@preact/signals expose .subscribe method as part of external interface (although undocumented), which makes it effectively a Subscribable - pattern shared by Rxjs, Observable and many variations.

Would that be within usignal's intention to get this uniformity feature by exposing that method?

@WebReflection
Copy link
Owner

I’m not sure I understand the need or what it solves … any example that could help me providing an answer?

@dy
Copy link
Author

dy commented Nov 19, 2022

One use-case is sube - it subscribes to any kind of reactive/observable, so that the reactive value can be used in eg. various template engines as field values. Eg. hyperf supports reactive fields in HTML fragments, or templize supports reactive fields in template-parts.

Sorry I may not have examples outside of projects within my scope, but speculatively anywhere where generic-observable values are supported usignal could be one of backing libs. I guess it's trivial to detect or wrap, just was curious if there's an intention to be a bit more uniform.

@WebReflection
Copy link
Owner

Can you give me an example of how it works in practice? Only “subscribers” right now are outer effects, so to me it looks like you can subscribe to a value by using the fx (also undocumented) utility to be notified when the value changed … these are lightweight and independent from effects chains (detached) so maybe that’s the same thing just with different name or implementation? I really need to see code to understand what is this about, I’m not crawling all those libraries here, mostly because I’ve no time

@dy
Copy link
Author

dy commented Nov 19, 2022

Seems same name, yes.

The example I'm having atm is:

// scope is dict of signals
let evaluate = parseExpr(expr)
const result = computed(() => evaluate(scope))
result.subscribe(update)

That's same as

// scope is dict of signals
let evaluate = parseExpr(expr)
effect(() => update(evaluate(scope)))

But the latter is a bit more difficult to grasp and it's less flexible in terms of chosing backing reactive library - computed seems to be broader pattern than effect, is it?

@WebReflection
Copy link
Owner

Computed here are lazy but I don’t know what’s update in your first example

@dy
Copy link
Author

dy commented Nov 20, 2022

update is directive-specific update function

@Mex505
Copy link

Mex505 commented Nov 20, 2022 via email

@WebReflection
Copy link
Owner

@Mex505 i don’t tolerate these kind of exchanges and I will block you if this happens again.

@dy
Copy link
Author

dy commented Nov 20, 2022

@WebReflection sorry couldn't elaborate yesterday from the phone. 'update' is a side-effect function, it is provided externally by user or library.

@dy
Copy link
Author

dy commented Nov 26, 2022

@WebReflection ok, just faced another scenario. Imagine persistency for simple todo-list.

We need to save todos to local storage any time list or any particular item updates.

let todos = signal([])

// direct effect here serializes any time todos change
effect(() => save(todos.value))

function addItem(text, done) {
  let item = { text: signal(text), done: signal(done) }
  todos.value = [...items.value, item]
  
  // indirect effects here save full list any time any todo item changes.
  item.text.subscribe(() => save(todos.value))
  item.done.subscribe(() => save(todos.value))
}

function save(items) {
  localStorage.setItem('todomvc.items', JSON.stringify(items))
}

How would you write indirect effects here? I imagine you'd need to artificially insert dependency like

  effect(() => (item.text, item.done, save(todos.peek())))

But then we have to use todos.peek() here to avoid extra updates.

@WebReflection
Copy link
Owner

You can run effects and detach effects via the exported Fx class … it looks like everything is there but I need to document and provide examples … I also need to check what preact does with signals but to me it looks like you want to subscribe to a signal and run something on its value changed, which is a different, straight to the point, api, imho.

subscribe(callback, …signals)

would likely be my pick

@dy
Copy link
Author

dy commented Nov 26, 2022

tbh I expected 'effect' to do that, just explicitly accepting dependencies as after-arguments.
'effect(callback, ...deps)'. That also looks somewhat like useEffect hook.

@WebReflection
Copy link
Owner

Except effects run if you access value which doesn’t happen by default or otherwise. Computed are lazy, so I really need time to understand what Preact does and it won’t happen today.

@WebReflection
Copy link
Owner

WebReflection commented Nov 28, 2022

@dy OK, I've had a look at Preact subscribe and I think this is all you need?

import {Fx, Signal} from 'usignal';

const options = {async: false, equals: true};
Signal.prototype.subscribe = function (fn) {
  const fx = new Fx(() => { fn(this.value); }, void 0, options).run();
  return () => { fx.stop() };
};

I am not sure this should be exported though, as I don't want to mimic strictly everything Preact does (like a fork) but especially not undocumented features.

@WebReflection
Copy link
Owner

P.S. please keep in mind computed here are lazy, meaning you won't likely get any notification unless you access these ... if it's Preact internals you are after though, I suggest you just use Preact for your project as lazy computed are here to stay for the time being (these were not lazy before, horrible performance and side-effects all over).

@WebReflection
Copy link
Owner

@dy closing as this seems to be solved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants