Mobx-state-tree: Make identifiers mutable

Created on 19 Jun 2018  路  5Comments  路  Source: mobxjs/mobx-state-tree

Mutating identifiers should be an exception, but potentially it would be nice if it was possible to make sure that we can swap client side identifiers for server side generated ones. Should

  • [ ] update identifier cache
  • [ ] update snapshot of all things referring to the identifier
  • [ ] not affect reconciliation
  • [ ] new identifier should not be taken already
brainstorminwild idea

Most helpful comment

Any update on this or workaround?
I have an optimistic update in my application and wonder if I could just reassign a new ID but not removing and adding a new item from the server.

All 5 comments

Thinking about it a bit... This would open up a possibility to support offline capabilities. A model can be assigned a transient ID while its being synced to the backend. It could very well be added to the store, the user would have an instant feedback, and still show some indication that the model has not synced yet. And when it has an ID from the backend it would update the references and clean up.
I could look into this more. Thanks for mobx and mst :)

@mweststrate did you have a try on this?
Heres what I have tried

identifier.ts

    reconcile(current: INode, newValue: string) {
        if (current.storedValue !== newValue && current.parent) {
            current.parent!.updateIdentifier(newValue)
        }
        return super.reconcile(current, newValue)
    }

object-node.ts

    updateIdentifier(newIdentifier: string) {
        this.identifier = newIdentifier
        this.root.identifierCache!.changeIdentifier(newIdentifier, this)
    }

identifier-cache.ts

    changeIdentifier(newIdentifier: string, node: ObjectNode) {
        const identifier = node.identifier!

        if (!this.cache.has(newIdentifier)) {
            this.cache.set(newIdentifier, observable.array<ObjectNode>([], mobxShallow))
        }

        const set = this.cache.get(newIdentifier)!
        if (set.indexOf(node) !== -1) fail(`Already registered`)
        set.push(node)

        const prevSet = this.cache.get(identifier)!
        if (prevSet.indexOf(node) == -1) fail(`Already removed`)
        prevSet.remove(node)
    }

Heres my test

test("references are updated when identifier change", () => {
    const values: number[] = []
    const Book = types.model({ id: types.identifier, price: types.number })
    const BookEntry = types.model({ book: types.reference(Book) }).views(self => ({
        get price() {
            return self.book.price * 2
        }
    }))
    const Store = types.model({
        books: types.array(Book),
        entries: types.optional(types.array(BookEntry), [])
    })

    const s = Store.create({ books: [{ id: "1", price: 3 }] })
    unprotect(s)

    s.books.push(Book.create({ id: "3", price: 2 }))

    s.entries.push(BookEntry.create({ book: "3" }))
    expect(s.entries[0].book.price).toBe(2)

    s.books[0].id = "6"
    s.entries.push(BookEntry.create({ book: "6" }))
    expect(s.entries[1].book.price).toBe(2)
})

It throws an exception at s.entries.push(BookEntry.create({ book: "6" })) because the cache update operation has not completed yet.
It seems direct assign to identifiers need to prevented.

Any update on this or workaround?
I have an optimistic update in my application and wonder if I could just reassign a new ID but not removing and adding a new item from the server.

This is absolutely necessary for offline capabilities. I'll follow this thread closely and I'll try to come up with ideas. The approach of @broncha seems interesting.

Any workarounds? I'd like to add offline capabilities to a MST app of mine but I'm a bit lost.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lidermanrony picture lidermanrony  路  3Comments

lishine picture lishine  路  4Comments

FredyC picture FredyC  路  3Comments

EricForgy picture EricForgy  路  4Comments

tahv0 picture tahv0  路  3Comments