diff --git a/packages/quark/src/reactivity.ts b/packages/quark/src/reactivity.ts
index 7bea77e94b..2ffdd3dc74 100644
--- a/packages/quark/src/reactivity.ts
+++ b/packages/quark/src/reactivity.ts
@@ -19,19 +19,14 @@ export namespace State {
subs: undefined,
subsTail: undefined,
}
- const read = (() => readState(node)) as Writable
- read.set = (value) => writeState(node, value)
- read.update = (f) => {
- // Read untracked: calling update inside a tracked evaluation must not
- // make the caller depend on (and re-trigger from) this state.
- const previous = swapActiveSub(undefined)
- try {
- writeState(node, f(readState(node)))
- } finally {
- activeSub = previous
- }
- }
- read.subscribe = (listener) => subscribeNode(node, read, listener)
+ // Methods are shared this-based functions rather than per-instance
+ // closures: one callable and one node per state, and every call site
+ // stays monomorphic on the shared method identity.
+ const read = (() => readState(node)) as Writable & Handle>
+ read.node = node
+ read.set = stateSet
+ read.update = stateUpdate
+ read.subscribe = sharedSubscribe
return read
}
}
@@ -47,12 +42,36 @@ export namespace Computed {
subs: undefined,
subsTail: undefined,
}
- const read = (() => readComputed(node)) as Readable
- read.subscribe = (listener) => subscribeNode(node, read, listener)
+ const read = (() => readComputed(node)) as Readable & Handle>
+ read.node = node
+ read.subscribe = sharedSubscribe
return read
}
}
+interface Handle {
+ node: Node
+}
+
+function stateSet(this: Handle>, value: A): void {
+ writeState(this.node, value)
+}
+
+function stateUpdate(this: Handle>, f: (value: A) => A): void {
+ // Read untracked: calling update inside a tracked evaluation must not
+ // make the caller depend on (and re-trigger from) this state.
+ const previous = swapActiveSub(undefined)
+ try {
+ writeState(this.node, f(readState(this.node)))
+ } finally {
+ activeSub = previous
+ }
+}
+
+function sharedSubscribe(this: (() => A) & Handle, listener: (value: A) => void): () => void {
+ return subscribeNode(this.node, this, listener)
+}
+
export namespace Transaction {
export function run(f: () => A): A {
batchDepth++