Skip to content

Commit 03815f5

Browse files
committed
fix internally tracked sibling positions earlier
previously, the fixup was done in the first "real" render, now this happens immediately in the render that transitions from ComponentRenderState::Hydration to ComponentRenderState::Render. We do so by threading through a reference to the sibling that needs a fix
1 parent b007219 commit 03815f5

File tree

14 files changed

+162
-114
lines changed

14 files changed

+162
-114
lines changed

packages/yew/src/app_handle.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::rc::Rc;
55

66
use web_sys::Element;
77

8-
use crate::dom_bundle::{BSubtree, DomSlot, DynamicDomSlot};
8+
use crate::dom_bundle::{BSubtree, DomSlot};
99
use crate::html::{BaseComponent, Scope, Scoped};
1010

1111
/// An instance of an application.
@@ -34,13 +34,9 @@ where
3434
scope: Scope::new(None),
3535
};
3636
let hosting_root = BSubtree::create_root(&host);
37-
app.scope.mount_in_place(
38-
hosting_root,
39-
host,
40-
DomSlot::at_end(),
41-
DynamicDomSlot::new_debug_trapped(),
42-
props,
43-
);
37+
let _ = app
38+
.scope
39+
.mount_in_place(hosting_root, host, DomSlot::at_end(), props);
4440

4541
app
4642
}
@@ -110,15 +106,17 @@ mod feat_hydration {
110106
let mut fragment = Fragment::collect_children(&host);
111107
let hosting_root = BSubtree::create_root(&host);
112108

109+
let mut previous_next_sibling = None;
113110
app.scope.hydrate_in_place(
114111
hosting_root,
115112
host.clone(),
116113
&mut fragment,
117-
DynamicDomSlot::new_debug_trapped(),
118114
Rc::clone(&props),
115+
&mut previous_next_sibling,
119116
);
120-
#[cfg(debug_assertions)] // Fix trapped next_sibling at the root
121-
app.scope.reuse(props, DomSlot::at_end());
117+
if let Some(previous_next_sibling) = previous_next_sibling {
118+
previous_next_sibling.reassign(DomSlot::at_end());
119+
}
122120

123121
// We remove all remaining nodes, this mimics the clear_element behaviour in
124122
// mount_with_props.

packages/yew/src/dom_bundle/bcomp.rs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -63,23 +63,16 @@ impl Reconcilable for VComp {
6363
key,
6464
..
6565
} = self;
66-
let internal_ref = DynamicDomSlot::new_debug_trapped();
67-
68-
let scope = mountable.mount(
69-
root,
70-
parent_scope,
71-
parent.to_owned(),
72-
slot,
73-
internal_ref.clone(),
74-
);
66+
67+
let (scope, internal_ref) = mountable.mount(root, parent_scope, parent.to_owned(), slot);
7568

7669
(
7770
internal_ref.to_position(),
7871
BComp {
7972
type_id,
73+
scope,
8074
own_position: internal_ref,
8175
key,
82-
scope,
8376
},
8477
)
8578
}
@@ -131,27 +124,27 @@ mod feat_hydration {
131124
parent_scope: &AnyScope,
132125
parent: &Element,
133126
fragment: &mut Fragment,
127+
prev_next_sibling: &mut Option<DynamicDomSlot>,
134128
) -> Self::Bundle {
135129
let VComp {
136130
type_id,
137131
mountable,
138132
key,
139133
..
140134
} = self;
141-
let internal_ref = DynamicDomSlot::new_debug_trapped();
142135

143-
let scoped = mountable.hydrate(
136+
let (scope, own_slot) = mountable.hydrate(
144137
root.clone(),
145138
parent_scope,
146139
parent.clone(),
147-
internal_ref.clone(),
148140
fragment,
141+
prev_next_sibling,
149142
);
150143

151144
BComp {
152145
type_id,
153-
scope: scoped,
154-
own_position: internal_ref,
146+
scope,
147+
own_position: own_slot,
155148
key,
156149
}
157150
}

packages/yew/src/dom_bundle/blist.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ impl Reconcilable for VList {
494494
#[cfg(feature = "hydration")]
495495
mod feat_hydration {
496496
use super::*;
497-
use crate::dom_bundle::{Fragment, Hydratable};
497+
use crate::dom_bundle::{DynamicDomSlot, Fragment, Hydratable};
498498

499499
impl Hydratable for VList {
500500
fn hydrate(
@@ -503,13 +503,14 @@ mod feat_hydration {
503503
parent_scope: &AnyScope,
504504
parent: &Element,
505505
fragment: &mut Fragment,
506+
prev_next_sibling: &mut Option<DynamicDomSlot>,
506507
) -> Self::Bundle {
507508
let (key, fully_keyed, vchildren) = self.split_for_blist();
508509

509510
let mut children = Vec::with_capacity(vchildren.len());
510511

511512
for child in vchildren.into_iter() {
512-
let child = child.hydrate(root, parent_scope, parent, fragment);
513+
let child = child.hydrate(root, parent_scope, parent, fragment, prev_next_sibling);
513514

514515
children.push(child);
515516
}

packages/yew/src/dom_bundle/bnode.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ impl fmt::Debug for BNode {
267267
#[cfg(feature = "hydration")]
268268
mod feat_hydration {
269269
use super::*;
270-
use crate::dom_bundle::{Fragment, Hydratable};
270+
use crate::dom_bundle::{DynamicDomSlot, Fragment, Hydratable};
271271

272272
impl Hydratable for VNode {
273273
fn hydrate(
@@ -276,17 +276,20 @@ mod feat_hydration {
276276
parent_scope: &AnyScope,
277277
parent: &Element,
278278
fragment: &mut Fragment,
279+
prev_next_sibling: &mut Option<DynamicDomSlot>,
279280
) -> Self::Bundle {
280281
match self {
281282
VNode::VTag(vtag) => RcExt::unwrap_or_clone(vtag)
282-
.hydrate(root, parent_scope, parent, fragment)
283+
.hydrate(root, parent_scope, parent, fragment, prev_next_sibling)
284+
.into(),
285+
VNode::VText(vtext) => vtext
286+
.hydrate(root, parent_scope, parent, fragment, prev_next_sibling)
283287
.into(),
284-
VNode::VText(vtext) => vtext.hydrate(root, parent_scope, parent, fragment).into(),
285288
VNode::VComp(vcomp) => RcExt::unwrap_or_clone(vcomp)
286-
.hydrate(root, parent_scope, parent, fragment)
289+
.hydrate(root, parent_scope, parent, fragment, prev_next_sibling)
287290
.into(),
288291
VNode::VList(vlist) => RcExt::unwrap_or_clone(vlist)
289-
.hydrate(root, parent_scope, parent, fragment)
292+
.hydrate(root, parent_scope, parent, fragment, prev_next_sibling)
290293
.into(),
291294
// You cannot hydrate a VRef.
292295
VNode::VRef(_) => {
@@ -303,9 +306,11 @@ mod feat_hydration {
303306
)
304307
}
305308
VNode::VSuspense(vsuspense) => RcExt::unwrap_or_clone(vsuspense)
306-
.hydrate(root, parent_scope, parent, fragment)
309+
.hydrate(root, parent_scope, parent, fragment, prev_next_sibling)
310+
.into(),
311+
VNode::VRaw(vraw) => vraw
312+
.hydrate(root, parent_scope, parent, fragment, prev_next_sibling)
307313
.into(),
308-
VNode::VRaw(vraw) => vraw.hydrate(root, parent_scope, parent, fragment).into(),
309314
}
310315
}
311316
}

packages/yew/src/dom_bundle/braw.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ impl Reconcilable for VRaw {
142142
#[cfg(feature = "hydration")]
143143
mod feat_hydration {
144144
use super::*;
145-
use crate::dom_bundle::{Fragment, Hydratable};
145+
use crate::dom_bundle::{DynamicDomSlot, Fragment, Hydratable};
146146
use crate::virtual_dom::Collectable;
147147

148148
impl Hydratable for VRaw {
@@ -152,15 +152,24 @@ mod feat_hydration {
152152
_parent_scope: &AnyScope,
153153
parent: &Element,
154154
fragment: &mut Fragment,
155+
prev_next_sibling: &mut Option<DynamicDomSlot>,
155156
) -> Self::Bundle {
156157
let collectable = Collectable::Raw;
157158
let fallback_fragment = Fragment::collect_between(fragment, &collectable, parent);
159+
let first_child = fallback_fragment.iter().next().cloned();
160+
161+
if let (Some(first_child), prev_next_sibling) = (&first_child, prev_next_sibling) {
162+
if let Some(prev_next_sibling) = prev_next_sibling {
163+
prev_next_sibling.reassign(DomSlot::at(first_child.clone()));
164+
}
165+
*prev_next_sibling = None;
166+
}
158167

159168
let Self { html } = self;
160169

161170
BRaw {
162171
children_count: fallback_fragment.len(),
163-
reference: fallback_fragment.iter().next().cloned(),
172+
reference: first_child,
164173
html,
165174
}
166175
}

packages/yew/src/dom_bundle/bsuspense.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ impl Reconcilable for VSuspense {
224224
#[cfg(feature = "hydration")]
225225
mod feat_hydration {
226226
use super::*;
227-
use crate::dom_bundle::{Fragment, Hydratable};
227+
use crate::dom_bundle::{DynamicDomSlot, Fragment, Hydratable};
228228
use crate::virtual_dom::Collectable;
229229

230230
impl Hydratable for VSuspense {
@@ -234,6 +234,7 @@ mod feat_hydration {
234234
parent_scope: &AnyScope,
235235
parent: &Element,
236236
fragment: &mut Fragment,
237+
previous_next_sibling: &mut Option<DynamicDomSlot>,
237238
) -> Self::Bundle {
238239
let detached_parent = document()
239240
.create_element("div")
@@ -250,9 +251,13 @@ mod feat_hydration {
250251

251252
// Even if initially suspended, these children correspond to the first non-suspended
252253
// content Refer to VSuspense::render_to_string
253-
let children_bundle =
254-
self.children
255-
.hydrate(root, parent_scope, &detached_parent, &mut nodes);
254+
let children_bundle = self.children.hydrate(
255+
root,
256+
parent_scope,
257+
&detached_parent,
258+
&mut nodes,
259+
previous_next_sibling,
260+
);
256261

257262
// We trim all leading text nodes before checking as it's likely these are whitespaces.
258263
nodes.trim_start_text_nodes();

packages/yew/src/dom_bundle/btag/mod.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ mod feat_hydration {
337337
use web_sys::Node;
338338

339339
use super::*;
340-
use crate::dom_bundle::{node_type_str, Fragment, Hydratable};
340+
use crate::dom_bundle::{node_type_str, DynamicDomSlot, Fragment, Hydratable};
341341

342342
impl Hydratable for VTag {
343343
fn hydrate(
@@ -346,6 +346,7 @@ mod feat_hydration {
346346
parent_scope: &AnyScope,
347347
_parent: &Element,
348348
fragment: &mut Fragment,
349+
prev_next_sibling: &mut Option<DynamicDomSlot>,
349350
) -> Self::Bundle {
350351
let tag_name = self.tag().to_owned();
351352

@@ -412,7 +413,12 @@ mod feat_hydration {
412413
}
413414
VTagInner::Other { children, tag } => {
414415
let mut nodes = Fragment::collect_children(&el);
415-
let child_bundle = children.hydrate(root, parent_scope, &el, &mut nodes);
416+
let mut prev_next_child = None;
417+
let child_bundle =
418+
children.hydrate(root, parent_scope, &el, &mut nodes, &mut prev_next_child);
419+
if let Some(prev_next_child) = prev_next_child {
420+
prev_next_child.reassign(DomSlot::at_end());
421+
}
416422

417423
nodes.trim_start_text_nodes();
418424

@@ -423,6 +429,10 @@ mod feat_hydration {
423429
};
424430

425431
node_ref.set(Some((*el).clone()));
432+
if let Some(prev_next_sibling) = prev_next_sibling {
433+
prev_next_sibling.reassign(DomSlot::at((*el).clone()));
434+
}
435+
*prev_next_sibling = None;
426436

427437
BTag {
428438
inner,

packages/yew/src/dom_bundle/btext.rs

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ mod feat_hydration {
9292
use web_sys::Node;
9393

9494
use super::*;
95-
use crate::dom_bundle::{Fragment, Hydratable};
95+
use crate::dom_bundle::{DynamicDomSlot, Fragment, Hydratable};
9696

9797
impl Hydratable for VText {
9898
fn hydrate(
@@ -101,9 +101,19 @@ mod feat_hydration {
101101
_parent_scope: &AnyScope,
102102
parent: &Element,
103103
fragment: &mut Fragment,
104+
previous_next_sibling: &mut Option<DynamicDomSlot>,
104105
) -> Self::Bundle {
105-
let next_sibling = if let Some(m) = fragment.front().cloned() {
106-
// better safe than sorry.
106+
let create_at = |next_sibling: Option<Node>, text: AttrValue| {
107+
// If there are multiple text nodes placed back-to-back in SSR, it may be parsed as a
108+
// single text node by browser, hence we need to add extra text nodes here
109+
// if the next node is not a text node. Similarly, the value of the text
110+
// node may be a combination of multiple VText vnodes. So we always need to
111+
// override their values.
112+
let text_node = document().create_text_node(text.as_ref());
113+
DomSlot::create(next_sibling).insert(parent, &text_node);
114+
BText { text, text_node }
115+
};
116+
let btext = if let Some(m) = fragment.front().cloned() {
107117
if m.node_type() == Node::TEXT_NODE {
108118
let m = m.unchecked_into::<TextNode>();
109119
// pop current node.
@@ -117,27 +127,21 @@ mod feat_hydration {
117127
// Please see the next comment for a detailed explanation.
118128
m.set_node_value(Some(self.text.as_ref()));
119129

120-
return BText {
130+
BText {
121131
text: self.text,
122132
text_node: m,
123-
};
133+
}
134+
} else {
135+
create_at(Some(m), self.text)
124136
}
125-
Some(m)
126137
} else {
127-
fragment.sibling_at_end().cloned()
138+
create_at(fragment.sibling_at_end().cloned(), self.text)
128139
};
129-
130-
// If there are multiple text nodes placed back-to-back in SSR, it may be parsed as a
131-
// single text node by browser, hence we need to add extra text nodes here
132-
// if the next node is not a text node. Similarly, the value of the text
133-
// node may be a combination of multiple VText vnodes. So we always need to
134-
// override their values.
135-
let text_node = document().create_text_node("");
136-
DomSlot::create(next_sibling).insert(parent, &text_node);
137-
BText {
138-
text: "".into(),
139-
text_node,
140+
if let Some(previous_next_sibling) = previous_next_sibling {
141+
previous_next_sibling.reassign(DomSlot::at(btext.text_node.clone().into()));
140142
}
143+
*previous_next_sibling = None;
144+
btext
141145
}
142146
}
143147
}

packages/yew/src/dom_bundle/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,9 @@ mod feat_hydration {
9494
parent: &Element,
9595
fragment: &mut Fragment,
9696
node: VNode,
97+
previous_next_sibling: &mut Option<DynamicDomSlot>,
9798
) -> Self {
98-
let bundle = node.hydrate(root, parent_scope, parent, fragment);
99+
let bundle = node.hydrate(root, parent_scope, parent, fragment, previous_next_sibling);
99100
Self(bundle)
100101
}
101102
}

0 commit comments

Comments
 (0)