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

implementation comparison #30

Closed
hex13 opened this issue Jan 4, 2018 · 1 comment
Closed

implementation comparison #30

hex13 opened this issue Jan 4, 2018 · 1 comment

Comments

@hex13
Copy link

hex13 commented Jan 4, 2018

I made similar library (also auto-immutability via Proxy) but my library takes quite a different approach to implementation.
https://github.com/hex13/enter-ghost/tree/master/packages/transmutable

I don’t create copies in set traps. Instead of that I only “record” mutations like they were events (I’ve got an array with list of “what happened”, e.g.

state.foo.bar = 3
state.baz = 4

get recorded as

[
  {type: 'set', path: ['foo', 'bar'], value: 3},
  {type: 'set', path: ['baz'], value: 4}
]

Then (after recording all mutations) I make a deep clone of the whole state tree (but I do dirty checking, based on recorded mutations - if something is not changed in mutation, it's copied by reference).

And then I replay recorded mutations and apply them to the clone of state.

On the other hand I've noticed that you took a different approach to implementation. From what I've seen you're making ad-hoc copies of state in Proxy and change them immediately (without recording mutations).

Which approach is better?

I think both have pros and cons.

Event-sourcing approach allows for recording all mutations. Probably in Redux setting it won't be
needed (because Redux is already kind of event-sourced) but I was experimenting with making my own state management library and event-sourcing allowed me to make such a nice dev tools for it which showed me which exact mutations occured: https://hex13.github.io/demos/todo/

Event-sourcing approach also can help with the versioning objects (undo, redo, clone, merge) but with Redux it could probably be done in some different way.

on the other hand because my implementation has two phases (1. record mutations 2. make a copy and replay mutations) and this has some shortcomings. Because it applies all changes all at once, this means state in transformer always point to old, stale version of state:

transmutable

transform({arr: [10,20]},  draft => {
    draft.arr.push(30);
    console.log(draft.arr); // [10, 20]
});

immer

immer({arr: [10,20]},  draft => {
    draft.arr.push(30);
    console.log(draft.arr); // [ 10, 20, <1 empty item> ]
});

This means that your approach (modify ad-hoc) feels more like VanillaJS. I make a push and something was pushed right away. Where in transmutable draft always returns values from old version.

I wonder if it was possible to merge these two approaches. I need event-sourcing* but your immediate approach has also clear advantages.

Besides yours and mine there are also similar libraries made by different people.
https://github.com/engineforce/ImmutableAssign
https://github.com/Palindrom/JSONPatcherProxy (I think this one only records mutations, without transforming).

*I'm intending to make full state management event-sourced library and transmutable is only a mean to an end.

@mweststrate
Copy link
Collaborator

I'll try to find time to answer the question later on, but the <1 empty item is actually a bug :) and appears only when handling non-freezed data. Will investigate!

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

2 participants