import { renderEffect } from './renderEffect'
import { DynamicFragment } from './fragment'
-const ifStack = [] as DynamicFragment[]
-const insertionParents = new WeakMap<DynamicFragment, Node[]>()
-
-/**
- * Collects insertionParents inside an if block during hydration
- * When the if condition becomes false on the client, clears the
- * HTML of these insertionParents to prevent duplicate rendering
- * results when the condition becomes true again
- *
- * Example:
- * const t2 = _template("<div></div>")
- * const n2 = _createIf(() => show.value, () => {
- * const n5 = t2()
- * _setInsertionState(n5)
- * const n4 = _createComponent(Comp) // renders `<span></span>`
- * return n5
- * })
- *
- * After hydration, the HTML of `n5` is `<div><span></span></div>` instead of `<div></div>`.
- * When `show.value` becomes false, the HTML of `n5` needs to be cleared,
- * to avoid duplicated rendering when `show.value` becomes true again.
- */
-export function collectInsertionParents(insertionParent: ParentNode): void {
- const currentIf = ifStack[ifStack.length - 1]
- if (currentIf) {
- let nodes = insertionParents.get(currentIf)
- if (!nodes) insertionParents.set(currentIf, (nodes = []))
- nodes.push(insertionParent)
- }
-}
-
export function createIf(
condition: () => any,
b1: BlockFn,
elseIf && isHydrating ? ELSE_IF_ANCHOR_LABEL : IF_ANCHOR_LABEL,
)
: new DynamicFragment()
- if (isHydrating) {
- ;(frag as DynamicFragment).teardown = () => {
- const nodes = insertionParents.get(frag as DynamicFragment)
- if (nodes) {
- nodes.forEach(p => ((p as Element).innerHTML = ''))
- insertionParents.delete(frag as DynamicFragment)
- }
- ;(frag as DynamicFragment).teardown = undefined
- }
- ifStack.push(frag as DynamicFragment)
- }
renderEffect(() => (frag as DynamicFragment).update(condition() ? b1 : b2))
- isHydrating && ifStack.pop()
}
if (!isHydrating) {
// TODO this should not happen
throw new Error('No current hydration node')
}
- node = adoptTemplate(currentHydrationNode!, html)!
- if (root) (node as any).$root = true
- return node
+ // do not cache the adopted node in node because it contains child nodes
+ // this avoids duplicate rendering of children
+ const adopted = adoptTemplate(currentHydrationNode!, html)!
+ if (root) (adopted as any).$root = true
+ return adopted
}
// fast path for text nodes
if (html[0] !== '<') {
* indicates forwarded slot
*/
forwarded?: boolean
- teardown?: () => void
anchorLabel?: string
constructor(anchorLabel?: string) {
// teardown previous branch
if (this.scope) {
this.scope.stop()
- if (parent) this.teardown && this.teardown()
const mode = transition && transition.mode
if (mode) {
applyTransitionLeaveHooks(this.nodes, transition, renderBranch)
-import { collectInsertionParents } from './apiCreateIf'
-import { isHydrating } from './dom/hydration'
-
export let insertionParent:
| (ParentNode & {
// number of prepends - hydration only
insertionParent = parent
insertionAnchor = anchor
insertionChildIndex = offset
-
- if (isHydrating) {
- collectInsertionParents(parent)
- }
}
export function resetInsertionState(): void {