Skip to content
This repository has been archived by the owner on Jun 3, 2024. It is now read-only.

Commit

Permalink
Memoize actions and calculates node dirtyness (#52)
Browse files Browse the repository at this point in the history
* memoize actions and calculates node dirtyness

* fix jdoc

* ignore detekt warning. this will be soon refactored, no pint in fixing
  • Loading branch information
Tiagoperes authored Aug 31, 2022
1 parent 6482671 commit ad15fac
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 10 deletions.
41 changes: 31 additions & 10 deletions src/commonMain/kotlin/com/zup/nimbus/core/render/Renderer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ class Renderer(
) {
private val logger = view.nimbusInstance.logger
private val structuralComponents = view.nimbusInstance.structuralComponents
// fixme: remove this once we have an efficient compilation process that runs before the first render of a node.
// intentional BUG: we're gonna recall the action using its key as the id. If an action has the same name as another
// action in the same node, this will bug out! Example: { onChange: { perform: action } },
// { onBlur: { perform: action } }: both actions are named "perform" and will be considered the same, although they
// shouldn't.
// Attention: this must be fixed before a stable version is released.
private val memoizedActions = HashMap<String, HashMap<String, (Any?) -> Unit>>()

/**
* Resolves the property "value" of "node" recursively.
Expand All @@ -30,6 +37,7 @@ class Renderer(
* @param extraStates use this to declare states that should be implicit.
* @return the resolved value.
*/
@Suppress("NestedBlockDepth")
private fun resolveProperty(
value: Any?,
key: String,
Expand All @@ -39,16 +47,21 @@ class Renderer(
if (value is List<*>) {
if (RenderAction.isActionList(value)) {
try {
return deserializeActions(
actionList = RenderAction.createActionList(value),
event = key,
node = node,
view = view,
extraStates = extraStates,
resolve = { propertyValue, propertyKey, implicitStates ->
resolveProperty(propertyValue, propertyKey, node, implicitStates)
},
)
val memoized = memoizedActions[node.id] ?: HashMap()
memoizedActions[node.id] = memoized
if (!memoized.containsKey(key)) {
memoized[key] = deserializeActions(
actionList = RenderAction.createActionList(value),
event = key,
node = node,
view = view,
extraStates = extraStates,
resolve = { propertyValue, propertyKey, implicitStates ->
resolveProperty(propertyValue, propertyKey, node, implicitStates)
}
)
}
return memoized[key]
} catch (error: MalformedActionListError) {
throw RenderingError(error.message)
}
Expand All @@ -71,9 +84,12 @@ class Renderer(
* @param node the node to resolve.
*/
private fun resolve(node: RenderNode) {
val previousHash = node.properties?.hashCode() ?: 0
node.properties = node.rawProperties?.mapValues {
resolveProperty(it.value, it.key, node, emptyList())
}
val nextHash = node.properties?.hashCode() ?: 0
node.dirty = node.dirty || previousHash != nextHash
node.isRendered = true
}

Expand Down Expand Up @@ -119,6 +135,11 @@ class Renderer(
if (componentBuilder == null) children.add(child)
else children.addAll(buildStructuralComponent(child, componentBuilder, node.stateHierarchy!!))
}
if (!node.dirty) {
val previousStructure = node.children?.map { it.id }
val nextStructure = children.map { it.id }
node.dirty = previousStructure != nextStructure
}
node.children = children
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class RenderNode(
implicitStates: Map<String, Any?>?,
stateId: String?,
stateValue: Any?,
override var dirty: Boolean = true,
): ServerDrivenNode {
companion object {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,9 @@ interface ServerDrivenNode {
* The children of this node. If this is a leaf-node, children will be null or an empty map.
*/
val children: List<ServerDrivenNode>?
/**
* Whether this node needs to be updated or not. If the UI layer decides to use this, it must set it to false once
* the node is rendered.
*/
var dirty: Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.zup.nimbus.core.OperationHandler
import com.zup.nimbus.core.ServerDrivenConfig
import com.zup.nimbus.core.ViewObserver
import com.zup.nimbus.core.observe
import com.zup.nimbus.core.tree.ServerDrivenNode
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
Expand Down Expand Up @@ -42,6 +43,7 @@ class PerformanceTest {
val inCartText = NodeUtils.findById(content, "in-cart:$productId")
assertNull(newButton)
assertEquals("In cart ✓", inCartText?.properties?.get("text"))
clean(content)
return elapsed
}

Expand All @@ -52,6 +54,11 @@ class PerformanceTest {
return "${intValue}.$decimalValue"
}

private fun clean(node: ServerDrivenNode) {
node.dirty = false
node.children?.forEach { clean(it) }
}

private suspend fun runPerformanceTest(jsonFileName: String, maxTimeMs: Int) {
val json = JsonLoader.loadJson(jsonFileName)
val nimbus = Nimbus(ServerDrivenConfig("", "test", operations = operations))
Expand All @@ -62,6 +69,7 @@ class PerformanceTest {
page.renderer.paint(node)
observer.waitForChanges()
val times = mutableListOf(Clock.System.now().toEpochMilliseconds() - started)
clean(observer.history.last())
for (i in 1..20) {
times.add(addToCart(observer, i, i + 1))
}
Expand Down

0 comments on commit ad15fac

Please sign in to comment.