Fix DOM-preserving hydration: text node mismatch + conditional markers
Two issues with the initial hydration implementation:
1. Text node mismatch: SSR merges adjacent text into one node
("0 / 16") but client renders three separate children. When the
cursor ran out, new nodes were created but dom-append was
unconditionally skipped. Fix: only skip append when the child
already has a parent (existing SSR node). New nodes (nil parent)
get appended even during hydration.
2. Conditional markers: dispatch-render-form for if/when/cond in
island scope was injecting comment markers during hydration,
corrupting the DOM. Fix: skip the reactive conditional machinery
during hydration — just evaluate and render the active branch
normally, walking the cursor. Reactivity for conditionals
activates after the first user-triggered re-render.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -375,7 +375,11 @@
|
||||
(fn () (render-to-dom arg env new-ns)))
|
||||
(spread? child)
|
||||
nil
|
||||
:else (when (not (hydrating?)) (dom-append el child)))))
|
||||
:else (when
|
||||
(or
|
||||
(not (hydrating?))
|
||||
(nil? (dom-parent child)))
|
||||
(dom-append el child)))))
|
||||
(assoc state "i" (inc (get state "i"))))))))
|
||||
(dict "i" 0 "skip" false)
|
||||
args)
|
||||
@@ -477,9 +481,18 @@
|
||||
((args :as list) (env :as dict) (ns :as string))
|
||||
(if
|
||||
(hydrating?)
|
||||
(do
|
||||
(for-each (fn (x) (render-to-dom x env ns)) args)
|
||||
(create-fragment))
|
||||
(let
|
||||
((frag (create-fragment)))
|
||||
(for-each
|
||||
(fn
|
||||
(x)
|
||||
(let
|
||||
((result (render-to-dom x env ns)))
|
||||
(when
|
||||
(and (not (spread? result)) (nil? (dom-parent result)))
|
||||
(dom-append frag result))))
|
||||
args)
|
||||
frag)
|
||||
(let
|
||||
((frag (create-fragment)))
|
||||
(for-each
|
||||
@@ -553,7 +566,7 @@
|
||||
(cond
|
||||
(= name "if")
|
||||
(if
|
||||
(island-scope?)
|
||||
(and (island-scope?) (not (hydrating?)))
|
||||
(let
|
||||
((marker (create-comment "r-if"))
|
||||
(current-nodes (list))
|
||||
@@ -617,7 +630,7 @@
|
||||
(create-fragment)))))
|
||||
(= name "when")
|
||||
(if
|
||||
(island-scope?)
|
||||
(and (island-scope?) (not (hydrating?)))
|
||||
(let
|
||||
((marker (create-comment "r-when"))
|
||||
(current-nodes (list))
|
||||
@@ -701,7 +714,7 @@
|
||||
frag)))
|
||||
(= name "cond")
|
||||
(if
|
||||
(island-scope?)
|
||||
(and (island-scope?) (not (hydrating?)))
|
||||
(let
|
||||
((marker (create-comment "r-cond"))
|
||||
(current-nodes (list))
|
||||
@@ -1080,7 +1093,9 @@
|
||||
(c)
|
||||
(let
|
||||
((result (render-to-dom c env ns)))
|
||||
(when (not (hydrating?)) (dom-append el result))))
|
||||
(when
|
||||
(or (not (hydrating?)) (nil? (dom-parent result)))
|
||||
(dom-append el result))))
|
||||
children)
|
||||
(do (when (hydrating?) (hydrate-exit-element)) el)))))
|
||||
(define
|
||||
@@ -1135,7 +1150,9 @@
|
||||
(c)
|
||||
(let
|
||||
((result (render-to-dom c env ns)))
|
||||
(when (not (hydrating?)) (dom-append el result))))
|
||||
(when
|
||||
(or (not (hydrating?)) (nil? (dom-parent result)))
|
||||
(dom-append el result))))
|
||||
children)
|
||||
(do (when (hydrating?) (hydrate-exit-element)) el)))))
|
||||
(define
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -375,7 +375,11 @@
|
||||
(fn () (render-to-dom arg env new-ns)))
|
||||
(spread? child)
|
||||
nil
|
||||
:else (when (not (hydrating?)) (dom-append el child)))))
|
||||
:else (when
|
||||
(or
|
||||
(not (hydrating?))
|
||||
(nil? (dom-parent child)))
|
||||
(dom-append el child)))))
|
||||
(assoc state "i" (inc (get state "i"))))))))
|
||||
(dict "i" 0 "skip" false)
|
||||
args)
|
||||
@@ -477,9 +481,18 @@
|
||||
((args :as list) (env :as dict) (ns :as string))
|
||||
(if
|
||||
(hydrating?)
|
||||
(do
|
||||
(for-each (fn (x) (render-to-dom x env ns)) args)
|
||||
(create-fragment))
|
||||
(let
|
||||
((frag (create-fragment)))
|
||||
(for-each
|
||||
(fn
|
||||
(x)
|
||||
(let
|
||||
((result (render-to-dom x env ns)))
|
||||
(when
|
||||
(and (not (spread? result)) (nil? (dom-parent result)))
|
||||
(dom-append frag result))))
|
||||
args)
|
||||
frag)
|
||||
(let
|
||||
((frag (create-fragment)))
|
||||
(for-each
|
||||
@@ -553,7 +566,7 @@
|
||||
(cond
|
||||
(= name "if")
|
||||
(if
|
||||
(island-scope?)
|
||||
(and (island-scope?) (not (hydrating?)))
|
||||
(let
|
||||
((marker (create-comment "r-if"))
|
||||
(current-nodes (list))
|
||||
@@ -617,7 +630,7 @@
|
||||
(create-fragment)))))
|
||||
(= name "when")
|
||||
(if
|
||||
(island-scope?)
|
||||
(and (island-scope?) (not (hydrating?)))
|
||||
(let
|
||||
((marker (create-comment "r-when"))
|
||||
(current-nodes (list))
|
||||
@@ -701,7 +714,7 @@
|
||||
frag)))
|
||||
(= name "cond")
|
||||
(if
|
||||
(island-scope?)
|
||||
(and (island-scope?) (not (hydrating?)))
|
||||
(let
|
||||
((marker (create-comment "r-cond"))
|
||||
(current-nodes (list))
|
||||
@@ -1080,7 +1093,9 @@
|
||||
(c)
|
||||
(let
|
||||
((result (render-to-dom c env ns)))
|
||||
(when (not (hydrating?)) (dom-append el result))))
|
||||
(when
|
||||
(or (not (hydrating?)) (nil? (dom-parent result)))
|
||||
(dom-append el result))))
|
||||
children)
|
||||
(do (when (hydrating?) (hydrate-exit-element)) el)))))
|
||||
(define
|
||||
@@ -1135,7 +1150,9 @@
|
||||
(c)
|
||||
(let
|
||||
((result (render-to-dom c env ns)))
|
||||
(when (not (hydrating?)) (dom-append el result))))
|
||||
(when
|
||||
(or (not (hydrating?)) (nil? (dom-parent result)))
|
||||
(dom-append el result))))
|
||||
children)
|
||||
(do (when (hydrating?) (hydrate-exit-element)) el)))))
|
||||
(define
|
||||
|
||||
Reference in New Issue
Block a user