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

Feature request: Watch #149

Closed
lxsmnsyc opened this issue Apr 30, 2021 · 14 comments · Fixed by #157
Closed

Feature request: Watch #149

lxsmnsyc opened this issue Apr 30, 2021 · 14 comments · Fixed by #157

Comments

@lxsmnsyc
Copy link
Contributor

lxsmnsyc commented Apr 30, 2021

watch (pending name) is a proposed function exportable from valtio that allows automatically tracking valtio instances instead of explicitly declaring the valtio instances you need to watch (subscribe) for. This is similar to useEffect except that you don't have to explicitly define the dependency list.

This proposed function allows you to easily compose subscriptions to valtio instances and not to individually watch them (subscribe).

Example

const count = proxy({ value: 0 });

const stop = watch(() => {
  // We mark `count` as our dependency. This
  // callback will then only run whenever count updates.
  console.log(`Count: ${count.value}`);
  
  return () => {
    // We may perform cleanups here when `stop` is called.
  };
});

count.value++; // Count: 1

Multiple subscriptions:

const recipient = proxy({ value: 'Dai Shi' });
const greeting = proxy({ value: 'Hello' });

watch(() => {
  console.log(`${greeting.value}, ${recipient.value}`);
});

recipient.value = 'John Doe'; // Hello, John Doe
greeting.value = 'Bonjour'; // Bonjour, John Doe
@dai-shi
Copy link
Member

dai-shi commented Apr 30, 2021

It would be nice if we could come up with the api without changing the core to cover the use case.

import { watch } from 'valtio/utils'

@lxsmnsyc
Copy link
Contributor Author

lxsmnsyc commented May 1, 2021

I'm not sure how you would be able to track dependencies while reading them without changing the core tho. I don't mind if this feature is part of another major release.

@dai-shi
Copy link
Member

dai-shi commented May 1, 2021

You may not like this, but this would work.

import { watch } from 'valtio/utils'

const stop = watch((get) => {
  // We mark `count` as our dependency. This
  // callback will then only run whenever count updates.
  console.log(`Count: ${get(count).value}`);
  
  return () => {
    // We may perform cleanups here when `stop` is called.
  };
});

With this little change, we can implement it without changing the core, which is a huge win for me.

@thelinuxlich
Copy link
Contributor

Loved this!

@AjaxSolutions
Copy link
Contributor

Does watch make the current subscribe feature obsolete?

@dai-shi
Copy link
Member

dai-shi commented May 1, 2021

The primary use case and the motivation of valtio is React (and its concurrent mode) and subscribe is a primitive feature for it. So, having watch in utils makes huge sense to me. It's the same idea not to have computed in vanilla (or any "read" functionalities).

@lxsmnsyc
Copy link
Contributor Author

lxsmnsyc commented May 2, 2021

Does watch make the current subscribe feature obsolete?

It doesn't. Even though both functions can "listen" to proxies, the timing of execution is different: watch runs immediately to detect dependencies then runs every time the proxies notify, while subscribe knows the dependency and runs only when notified not on initial call.

With this little change, we can implement it without changing the core, which is a huge win for me.

I agree, without changing the core, this by far the closest implementation we can get.

@dai-shi
Copy link
Member

dai-shi commented May 12, 2021

#149 (comment)
Would you or anyone like to implement such a util?

@lxsmnsyc
Copy link
Contributor Author

#149 (comment)
Would you or anyone like to implement such a util?

I would probably create an implementation this weekend 😅

@italodeandra
Copy link
Contributor

Thanks for the implementation @lxsmnsyc. The code looks dope.

One question though. Why would I use subscribe now that watch exists? My use cases seems that they can all be replaced by it.

I understand that they're different, but I can't see any use case that subscribe is better than watch. Perhaps performance wise? Is watch "heavier"?

@dai-shi
Copy link
Member

dai-shi commented May 21, 2021

(Seems like you already reached the conclusion?) I would use subscribe if it's enough.

@italodeandra
Copy link
Contributor

Seems like you already reached the conclusion?

I just want to be sure that I did misunderstand the watch purpose 😂

I would use subscribe if it's enough.

Indeed. But are they really that different in performance? I don't how to compare it.

@dai-shi
Copy link
Member

dai-shi commented May 21, 2021

Well, you would need to measure them to know the real performance.
Obviously, watch is built on top of subscribe, so it's slower. But, I believe it's unnoticeable in most use cases.

@lxsmnsyc
Copy link
Contributor Author

lxsmnsyc commented May 21, 2021

Why would I use subscribe now that watch exists?

Already asked by @AjaxSolutions, watch and subscribe have different "subscription" timings: watch immediately (synchronously) invokes the callback, this is to detect the dependencies as early as possible, while subscribe is lazy as the callback attached to subscribe is only invoked when the state changes.

Is watch heavier

watch takes advantage of the subscribe feature, so yes.

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

Successfully merging a pull request may close this issue.

5 participants