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
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.
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.