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

Mutable state frontend API proposal #443

Open
douira opened this issue Oct 23, 2021 · 2 comments
Open

Mutable state frontend API proposal #443

douira opened this issue Oct 23, 2021 · 2 comments

Comments

@douira
Copy link

douira commented Oct 23, 2021

This is my idea of an API that uses mutable state for compatibility with frameworks such as Vue.js that use mutable state. @ept suggested I write a brief proposal of how I think a mutable state API could work.

Mutable State

One idea would be to use a similar .change as for the immutable API but with no new document being returned. No mutable document needs to be passed into the callback since the document itself is mutable. If, for some reason, this is necessary because of implementation details that would also be ok. I like the callback idea because it delineates where a change starts and where it ends.

const doc = Automerge.init()
Automerge.change(doc, () => {
  doc.foo = 4
})

An even more extreme API might not have a .change at all with change tracking happening entirely through Proxies observing changes. Again, as I'm not at all familiar with the internals of Automerge, I don't know if this is feasible.

const doc = Automerge.init()
doc.foo = 4

Vue integration

A mutable Automerge state could be made reactive using Vue 3's reactive or by passing it to a Vue 2 component's data property. It's a little easier with Vue 3 than with Vue 2 due to how the new Vue's reactivity system is decoupled from the component system. Because Vue 2 doesn't use Proxies but rather getters and setters on objects, a state object present on the component will be the same as the original Automerge state. Vue 2 making the given object reactive might break Automerge because it adds a (hidden) property and replaces all original properties with getters/setters.

Vue 3 creates reactive objects with reactive and returns the object wrapped in a proxy. An integration with Vue 3 could work like this:

import { reactive } from 'vue'
const doc = Automerge.init()
const reactiveDoc = reactive(doc)
Automerge.change(reactiveDoc, () => {
  reactiveDoc.foo = 4
})
//reactiveDoc used and further modified by a Vue component

Automerge might not like that a proxy has been wrapped around the original state. Maybe the mutable state frontend could be built to be ok with this. I'm not sure how initializing a vue-reactive Automerge state synchronized from a server would work.

@BrianHung
Copy link

BrianHung commented Jan 29, 2022

Again, as I'm not at all familiar with the internals of Automerge, I don't know if this is feasible.

I would look at zustand and valtio which have similar apis.

Automerge might not like that a proxy has been wrapped around the original state.

For Vue (or React) integration, it might be better to keep the reactive object and auto merge state apart even though it’s redundant. Then create a bridge that syncs changes between the two. Example is valtio-yjs.

On that subject, as an option in the bridge should be a way to mark some fields excluded or included from syncing.

An even more extreme API might not have a .change at all with change tracking happening entirely through Proxies observing changes. Again, as I'm not at all familiar with the internals of Automerge, I don't know if this is feasible.

const doc = Automerge.init()
doc.foo = 4

One drawback of this mutable approach is losing the ability to group mutations together semantically e.g. with a commit message. There needs to be a way to batch things together within an enclosure or a "transaction"; this would be good for undo redo operations too.

@douira
Copy link
Author

douira commented Feb 13, 2022

const doc = Automerge.init()
doc.foo = 4

Yes, this is the kind of API I was thinking of. Maybe transaction features could be brought into this by marking the start and end of a transaction explicitly. Something like this (just a very rough idea):

const doc = Automerge.init()
doc.foo = 4 //implicit change

//explicit change
Automerge.startChange(doc)
doc.bar = 3
Automerge.commit(doc, "message")

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

No branches or pull requests

3 participants