Deep observability makes an object and the objects and arrays nested inside it observable as well. Changes anywhere in an observable AST can therefore notify subscribed observers. section 5.1.6 section 7.4
Apply deep observability to a reactive projection by:
- Wrapping the rental AST with MobX observable before passing it to the projection. section 5.1.6
- Marking the projection component as an observer so nested edits trigger updates. section 5.1.6
- Using observable edit state and actions when updating nested values such as record and attribute names. section 5.1.6
import { observable } from "mobx" #1
// ...other existing code...
createRoot(document.getElementById("root"))
.render(
<Projection
astObject={observable(rental)} #2
/>
)
Wraps the rental AST in observable at the React render boundary. section 5.1.6
import { observer } from "mobx-react"
// ...other existing code...
const Projection = observer(({ astObject, parent }) => {
// ...all other existing code...
})
Connects the projection component to observable changes through MobX observer. section 5.1.6
Deep conversion does not understand semantic reference relations. Applying observable directly to a circular AST can recurse until the call stack is exhausted. Construct the observable nodes first, then assign reference targets, or deserialize while explicitly resolving references. section 7.4
Listing 7.24 Making a circular AST observable—the right way
const { observable } = require("mobx")
const observableAstObject = observable({ #1
id: "IdOfFooObject",
concept: "Foo",
settings: {
"self-ref.": observable({}) #2
}
})
observableAstObject.settings["self-ref."].ref = observableAstObject #3
Builds a circular observable AST safely by creating observable nodes before assigning the self-reference. section 7.4
Listing 7.25 Deserializing to an observable AST
const { observable } = require("mobx") #1
const deserializeObservably = (serializedAst) => {
const id2AstObject = {}
const referencesToResolve = []
const deserializeInternal = (value) => { #2
if (isAstObject(value)) {
const astObject = observable({ #3
id: value.id,
concept: value.concept,
settings: {}
})
for (const propertyName in value.settings) {
astObject.settings[propertyName] =
deserializeInternal(
value.settings[propertyName])
}
id2AstObject[value.id] = astObject
return astObject
}
if (isSerializedAstReference(value)) {
const refObjectToFix = observable({}) #4
referencesToResolve.push([ value.refId, refObjectToFix ])
return refObjectToFix #5
}
if (Array.isArray(value)) {
return observable( #6
value.map(deserializeInternal))
}
return value
}
const deserializedAst = deserializeInternal(serializedAst)
referencesToResolve.forEach(
([ refId, refObjectToFix ]) => {
refObjectToFix.ref = id2AstObject[refId] #7
})
return deserializedAst
}
module.exports.deserializeObservably = #8
deserializeObservably
Deserializes AST objects into observable nodes while retaining deferred reference resolution. section 7.4
