/** * Repro: investigate rendering failures with reactive composition * * Three suspected runtime warts surfaced while rewriting cozy-poll's UI * (see branch `scoped-cells-cozy-poll`, commit 93d545ad6): * * W1. `style={computed(...)}` returning a style object — pattern renders blank * W2. `style={computed(...)}` returning an inline-style string — same * W3. Multiple top-level `{computed(() =>
)}` blocks combined — same * W4. `` inside `{computed(() =>
)}` — produces a * runtime error "Bidirectionally bound property $value is not reactive" * * This file is a side-by-side harness: each section is gated by a top-level * boolean flag so we can flip it on, redeploy, and see which one crashes the * whole pattern render. * * Default state below has ONLY safe variants enabled. Toggle the flags one at * a time, redeploy via `cf piece setsrc`, and screenshot the result. */ import { computed, Default, handler, NAME, pattern, type PerSpace, Stream, UI, type VNode, Writable, } from "commonfabric"; // ============================================================================ // Toggle these flags one at a time and redeploy with `cf piece setsrc` // ============================================================================ const ENABLE_W1_STYLE_COMPUTED_OBJECT = true; const ENABLE_W2_STYLE_COMPUTED_STRING = true; const ENABLE_W3_MULTIPLE_COMPUTED_VNODES = true; const ENABLE_W4_CF_INPUT_INSIDE_COMPUTED = true; type CounterCell = Writable>; type NameCell = Writable>; const tick = handler, { counter: CounterCell }>( (_, { counter }) => { counter.set((counter.get() ?? 0) + 1); }, ); export interface ReproInput { counter?: PerSpace>; name?: PerSpace>; } export interface ReproOutput { [NAME]: string; [UI]: VNode; counter: number; tick: Stream>; } export default pattern(({ counter, name }) => { const boundTick = tick({ counter }); return { [NAME]: "Computed-VNode blank-render repro", [UI]: (

Reactive composition repro

Counter: {counter}
Tick { /* Baseline reference: this ALWAYS renders. If we see this and the page is otherwise blank, the variants are the cause. */ }
Baseline (always rendered)
If you see this line, JSX is rendering. If not, the pattern crashed.
{/* W1: style={computed(...)} returning an object */}
W1 — style={computed(…)} → object
{ENABLE_W1_STYLE_COMPUTED_OBJECT ? (
({ padding: "8px", background: (counter ?? 0) % 2 === 0 ? "#dbeafe" : "#fde68a", borderRadius: "4px", }))} > W1 content; bg toggles every tick.
) : (
W1 disabled (ENABLE_W1_STYLE_COMPUTED_OBJECT = false)
)} {/* W2: style={computed(...)} returning a string */}
W2 — style={computed(…)} → string
{ENABLE_W2_STYLE_COMPUTED_STRING ? (
`padding: 8px; background: ${ (counter ?? 0) % 2 === 0 ? "#dbeafe" : "#fde68a" }; border-radius: 4px;` )} > W2 content; bg toggles every tick.
) : (
W2 disabled (ENABLE_W2_STYLE_COMPUTED_STRING = false)
)} {/* W3: many top-level computed-VNode blocks */}
W3 — multiple computed-VNode blocks
{ENABLE_W3_MULTIPLE_COMPUTED_VNODES ? (
{computed(() => (
Block A — counter is{" "} {(counter ?? 0) > 0 ? "positive" : "zero"}
))} {computed(() => (
Block B — counter mod 2 is{" "} {(counter ?? 0) % 2}
))} {computed(() => (
Block C — counter squared is{" "} {(counter ?? 0) * (counter ?? 0)}
))} {computed(() => (
Block D — counter == 5? {(counter ?? 0) === 5 ? "yes" : "no"}
))}
) : (
W3 disabled (ENABLE_W3_MULTIPLE_COMPUTED_VNODES = false)
)} {/* W4: cf-input $value= inside a computed block */}
W4 — cf-input $value= inside computed
{ENABLE_W4_CF_INPUT_INSIDE_COMPUTED ? (
{computed(() => (
Expect "$value not reactive" runtime error:
))}
) : (
W4 disabled (ENABLE_W4_CF_INPUT_INSIDE_COMPUTED = false)
)}
), counter: counter ?? 0, tick: boundTick, }; });