This value, which is hard to see when the object is created, is very obvious when the object is modified.
With a read-only tree, it’s very deep, and it’s not easy to modify parts.
First, immer can create a draft proxy for the root of the tree. If the object has some properties of other objects or collections, it can create more leaf sub-proxy as needed according to the user’s access method.
No matter object or a collection, we can get proxies, and although the API exposed by these proxies is writable, these proxies are initialized by readonly data without expensive cost. If the user modifies them, they will be upgraded to real writable objects by “copy/write” strategy.
Finally, when the whole process is over, all proxies are used to create new read-only objects
- If proxies are not upgraded, directly reuse the read-only data immutable objects they wrap(reuse subtree)
- If proxies have been upgraded, create immutable objects by the new data they hold(replace subtree)
This value, which is hard to see when the object is created, is very obvious when the object is modified.
val book2 = new(BookDraft.Sync::class, book) { // Change book -> book2
store().area().parent().name += "*"
println(authors.size) // For authors, only read, not write
}
In the above example, I only modified the many-to-one association “store”, don’t modified the one-to-many association “authors”.
In the new book2, store are replaced with new object(replace sub-tree), but authors are still the old list(reuse-subtree).
If the tree is shallow, you won’t see any difference between this approach and the copy function of the kotlin data class. But if the tree is deep, you will find that it is a gift from God.
This is immer, building a read-only object tree by modifying the mutable object tree. That’s why immer got the “Most impactful contribution JavaScript open source award in 2019” and why I ported it to kotlin/jvm.