natemoo-re / exo-store

6 stars 0 forks source link

`freeze()` is computationally expensive (CPU and memory), and can occur frequently #1

Open danielweck opened 3 years ago

danielweck commented 3 years ago

Hello :)

Thanks for making your experiment open-source. I know there are tons of (too many?) state containers out there, but I like to see a fresh take on an old problem ;)

I am sure you are already well-aware of the shortcomings of the following technique:

https://github.com/natemoo-re/exo-store/blob/42c1c1a0ad848345f118ec3fea8d842b36db566a/packages/core/index.ts#L178-L180

https://github.com/natemoo-re/exo-store/blob/42c1c1a0ad848345f118ec3fea8d842b36db566a/packages/preact/index.ts#L21-L38

... but I am opening this issue to share some thoughts about how to efficiently create a read-only snapshot of the current state (also, the problem of handling complex / non-primitive types like WeakMap comes to mind too).

At the moment I am using Valtio from @pmndrs , which is a lightweight Proxy-based state solution as well. It is a relatively immature lib, not heavily deployed / tested in the wild (so we're in good company :)

Valtio's "snapshot" creation is a little more sophisticated:

https://github.com/pmndrs/valtio/blob/265aa8070eb6ffbc9f62160db346209edc517f7c/src/vanilla.ts#L58-L94

https://github.com/pmndrs/valtio/blob/265aa8070eb6ffbc9f62160db346209edc517f7c/src/react.ts#L136

Regarding unsupported Proxy state types (yikes, but expected):

https://github.com/pmndrs/valtio/blob/265aa8070eb6ffbc9f62160db346209edc517f7c/src/vanilla.ts#L15-L26

Finally, I was studying exo-store source code and I was wondering about diffing the Proxy state. Valtio relies on a library called proxy-compare to check for state changes (on component mount):

https://github.com/pmndrs/valtio/blob/265aa8070eb6ffbc9f62160db346209edc517f7c/src/react.ts#L142-L147

https://github.com/dai-shi/proxy-compare/blob/70d611cca4daf41fd12d580b992f05c142da5562/src/index.ts#L240-L244

Am I right to say that out of the box, exo-store doesn't diff the Proxy data for a given state selector?

https://github.com/natemoo-re/exo-store/blob/42c1c1a0ad848345f118ec3fea8d842b36db566a/packages/core/index.ts#L160-L170

Sorry if this is a stupid question, I just want to make sure I understand the underlying logic.

natemoo-re commented 3 years ago

Thanks for opening this issue! I'll give it a more thorough read and respond point-by-point as soon as I can.

For now, I'll say that Exo is definitely using plenty of naive techniques. I really appreciate the thorough breakdown and comparison to hopefully address some of these! I have no doubt Valtio has great reasons for doing all the work they're doing for snapshots.

In an ideal world, there wouldn't be a separate, frozen snapshot at all. Reading and writing the Store directly was actually working well until I ran into trouble with Vue's Composition API. Maybe there's a better API that I haven't thought of yet!

danielweck commented 3 years ago

If I understand correctly based on Valtio's readme and issue tracker discussions, support for concurrent mode is a design goal, and async state properties are already supported via suspense ... preserving the integrity of the proxy state via a frozen / snapshot makes sense.

Once again, thanks for putting exo-store in GitHub, the codebase is giving me food for thoughts. I am relatively new to proxy-driven state containers, strategies for dependency tracking (computed / derived values), etc.

I'm looking for a lightweight solution to match my Preact project, laser-focused on essential functionality (no feature creep). I've looked into MobX (obviously), and Immer proxy techniques. There's some excellent prior art but just a bit too "invasive" with large API surface.

natemoo-re commented 3 years ago

Ahh that makes sense. Not sure async values makes sense for Exo since frameworks have a lot of different opinions in that area.


As promised, here's a more thorough response!

I am sure you are already well-aware of the shortcomings of the following technique:

100%. Quick hack. Like I said, I'm not entirely sold on the "snapshot" API but it very well could be the best option. Still exploring.

Am I right to say that out of the box, exo-store doesn't diff the Proxy data for a given state selector?

Hmm, Valtio's approach is interesting. Not sure I totally understand why they are doing a deep equality check on the snapshot. exo-store is doing a strict equality check in the set handler and only calling subscribers if changes are detected for a given path. I could be totally wrong, but I think tracking the path of every update accomplishes the same thing without requiring the deep equality check?