Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delay Hydration second render until all assistive nodes have been removed #2629

Merged
merged 22 commits into from
Apr 24, 2022
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/js_callback/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"

[dependencies]
wasm-bindgen = "0.2"
yew = { path = "../../packages/yew", features = ["csr"] }
yew = { path = "../../packages/yew", features = ["csr", "tokio"] }
hamza1311 marked this conversation as resolved.
Show resolved Hide resolved
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
once_cell = "1"
29 changes: 10 additions & 19 deletions packages/yew/src/dom_bundle/bcomp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ use crate::NodeRef;
pub(super) struct BComp {
type_id: TypeId,
scope: Box<dyn Scoped>,
// A internal NodeRef passed around to track this components position. This
// is "stable", i.e. does not change when reconciled.
internal_ref: NodeRef,
// The user-passed NodeRef from VComp. Might change every time we reconcile.
// Gets linked to the internal ref
node_ref: NodeRef,
key: Option<Key>,
}
Expand All @@ -44,8 +39,10 @@ impl ReconcileTarget for BComp {
self.scope.destroy_boxed(parent_to_detach);
}

fn shift(&self, next_parent: &Element, next_sibling: NodeRef) {
fn shift(&self, next_parent: &Element, next_sibling: NodeRef) -> NodeRef {
self.scope.shift_node(next_parent.clone(), next_sibling);

self.node_ref.clone()
}
}

Expand All @@ -65,23 +62,20 @@ impl Reconcilable for VComp {
node_ref,
key,
} = self;
let internal_ref = NodeRef::default();
node_ref.link(internal_ref.clone());

let scope = mountable.mount(
root,
internal_ref.clone(),
node_ref.clone(),
parent_scope,
parent.to_owned(),
next_sibling,
);

(
internal_ref.clone(),
node_ref.clone(),
BComp {
type_id,
node_ref,
internal_ref,
key,
scope,
},
Expand Down Expand Up @@ -123,10 +117,10 @@ impl Reconcilable for VComp {
} = self;

bcomp.key = key;
let old_ref = std::mem::replace(&mut bcomp.node_ref, node_ref);
let old_ref = std::mem::replace(&mut bcomp.node_ref, node_ref.clone());
bcomp.node_ref.reuse(old_ref);
mountable.reuse(bcomp.scope.borrow(), next_sibling);
bcomp.internal_ref.clone()
mountable.reuse(node_ref.clone(), bcomp.scope.borrow(), next_sibling);
node_ref
}
}

Expand All @@ -149,24 +143,21 @@ mod feat_hydration {
node_ref,
key,
} = self;
let internal_ref = NodeRef::default();
node_ref.link(internal_ref.clone());

let scoped = mountable.hydrate(
root.clone(),
parent_scope,
parent.clone(),
fragment,
internal_ref.clone(),
node_ref.clone(),
);

(
internal_ref.clone(),
node_ref.clone(),
BComp {
type_id,
scope: scoped,
node_ref,
internal_ref,
key,
},
)
Expand Down
12 changes: 8 additions & 4 deletions packages/yew/src/dom_bundle/blist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,10 +380,14 @@ impl ReconcileTarget for BList {
}
}

fn shift(&self, next_parent: &Element, next_sibling: NodeRef) {
for node in self.rev_children.iter().rev() {
node.shift(next_parent, next_sibling.clone());
fn shift(&self, next_parent: &Element, next_sibling: NodeRef) -> NodeRef {
let mut next_sibling = next_sibling;

for node in self.rev_children.iter() {
next_sibling = node.shift(next_parent, next_sibling.clone());
}

next_sibling
}
}

Expand Down Expand Up @@ -480,7 +484,7 @@ mod feat_hydration {
let (child_node_ref, child) = child.hydrate(root, parent_scope, parent, fragment);

if index == 0 {
node_ref.link(child_node_ref);
node_ref.reuse(child_node_ref);
}

children.push(child);
Expand Down
4 changes: 3 additions & 1 deletion packages/yew/src/dom_bundle/bnode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl ReconcileTarget for BNode {
}
}

fn shift(&self, next_parent: &Element, next_sibling: NodeRef) {
fn shift(&self, next_parent: &Element, next_sibling: NodeRef) -> NodeRef {
match self {
Self::Tag(ref vtag) => vtag.shift(next_parent, next_sibling),
Self::Text(ref btext) => btext.shift(next_parent, next_sibling),
Expand All @@ -72,6 +72,8 @@ impl ReconcileTarget for BNode {
next_parent
.insert_before(node, next_sibling.get().as_ref())
.unwrap();

NodeRef::new(node.clone())
}
Self::Portal(ref vportal) => vportal.shift(next_parent, next_sibling),
Self::Suspense(ref vsuspense) => vsuspense.shift(next_parent, next_sibling),
Expand Down
4 changes: 3 additions & 1 deletion packages/yew/src/dom_bundle/bportal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ impl ReconcileTarget for BPortal {
self.node.detach(&self.inner_root, &self.host, false);
}

fn shift(&self, _next_parent: &Element, _next_sibling: NodeRef) {
fn shift(&self, _next_parent: &Element, next_sibling: NodeRef) -> NodeRef {
// portals have nothing in it's original place of DOM, we also do nothing.

next_sibling
}
}

Expand Down
14 changes: 4 additions & 10 deletions packages/yew/src/dom_bundle/bsuspense.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,12 @@ impl ReconcileTarget for BSuspense {
}
}

fn shift(&self, next_parent: &Element, next_sibling: NodeRef) {
fn shift(&self, next_parent: &Element, next_sibling: NodeRef) -> NodeRef {
match self.fallback.as_ref() {
Some(Fallback::Bundle(bundle)) => {
bundle.shift(next_parent, next_sibling);
}
Some(Fallback::Bundle(bundle)) => bundle.shift(next_parent, next_sibling),
#[cfg(feature = "hydration")]
Some(Fallback::Fragment(fragment)) => {
fragment.shift(next_parent, next_sibling);
}
None => {
self.children_bundle.shift(next_parent, next_sibling);
}
Some(Fallback::Fragment(fragment)) => fragment.shift(next_parent, next_sibling),
None => self.children_bundle.shift(next_parent, next_sibling),
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion packages/yew/src/dom_bundle/btag/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,12 @@ impl ReconcileTarget for BTag {
}
}

fn shift(&self, next_parent: &Element, next_sibling: NodeRef) {
fn shift(&self, next_parent: &Element, next_sibling: NodeRef) -> NodeRef {
next_parent
.insert_before(&self.reference, next_sibling.get().as_ref())
.unwrap();

self.node_ref.clone()
}
}

Expand Down
4 changes: 3 additions & 1 deletion packages/yew/src/dom_bundle/btext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ impl ReconcileTarget for BText {
}
}

fn shift(&self, next_parent: &Element, next_sibling: NodeRef) {
fn shift(&self, next_parent: &Element, next_sibling: NodeRef) -> NodeRef {
let node = &self.text_node;

next_parent
.insert_before(node, next_sibling.get().as_ref())
.unwrap();

NodeRef::new(self.text_node.clone().into())
}
}

Expand Down
7 changes: 6 additions & 1 deletion packages/yew/src/dom_bundle/fragment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,16 @@ impl Fragment {
}

/// Shift current Fragment into a different position in the dom.
pub fn shift(&self, next_parent: &Element, next_sibling: NodeRef) {
pub fn shift(&self, next_parent: &Element, next_sibling: NodeRef) -> NodeRef {
for node in self.iter() {
next_parent
.insert_before(node, next_sibling.get().as_ref())
.unwrap();
}

self.front()
.cloned()
.map(NodeRef::new)
.unwrap_or(next_sibling)
}
}
2 changes: 1 addition & 1 deletion packages/yew/src/dom_bundle/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub(super) trait ReconcileTarget {
/// Move elements from one parent to another parent.
/// This is for example used by `VSuspense` to preserve component state without detaching
/// (which destroys component state).
fn shift(&self, next_parent: &Element, next_sibling: NodeRef);
fn shift(&self, next_parent: &Element, next_sibling: NodeRef) -> NodeRef;
}

/// This trait provides features to update a tree by calculating a difference against another tree.
Expand Down
5 changes: 1 addition & 4 deletions packages/yew/src/dom_bundle/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ pub(super) fn insert_node(node: &Node, parent: &Element, next_sibling: Option<&N
match next_sibling {
Some(next_sibling) => parent
.insert_before(node, Some(next_sibling))
.unwrap_or_else(|err| {
gloo::console::error!("failed to insert node", err, parent, next_sibling, node);
panic!("failed to insert tag before next sibling")
}),
.expect("failed to insert tag before next sibling"),
None => parent.append_child(node).expect("failed to append child"),
};
}
Expand Down
11 changes: 5 additions & 6 deletions packages/yew/src/functional/hooks/use_effect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ where
/// # Example
///
/// ```rust
/// use yew::{function_component, html, Html, Properties, use_effect_with_deps};
/// use yew::{function_component, html, use_effect_with_deps, Html, Properties};
/// # use gloo::console::log;
///
/// #[derive(Properties, PartialEq)]
Expand Down Expand Up @@ -189,16 +189,15 @@ where
///
/// ## Only on first render
///
/// Provide a empty tuple `()` as dependencies when you need to do something only on the first render
/// of a component.
/// Provide a empty tuple `()` as dependencies when you need to do something only on the first
/// render of a component.
///
/// ```rust
/// use yew::{function_component, html, Html, use_effect_with_deps};
/// use yew::{function_component, html, use_effect_with_deps, Html};
/// # use gloo::console::log;
///
/// #[function_component]
/// fn HelloWorld() -> Html {
///
/// use_effect_with_deps(
/// move |_| {
/// log!("I got rendered, yay!");
Expand All @@ -217,7 +216,7 @@ where
/// It will only get called when the component is removed from view / gets destroyed.
///
/// ```rust
/// use yew::{function_component, html, Html, use_effect_with_deps};
/// use yew::{function_component, html, use_effect_with_deps, Html};
/// # use gloo::console::log;
///
/// #[function_component]
Expand Down
12 changes: 6 additions & 6 deletions packages/yew/src/functional/hooks/use_force_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ impl UseForceUpdate {
///
/// # Use-case
///
/// Use this hook when wrapping an API that doesn't expose precise subscription events for fetched data.
/// You could then, at some point, invalidate your local cache of the fetched data and trigger a re-render
/// to let the normal render flow of components tell you again which data to fetch, and repopulate the
/// cache accordingly.
/// Use this hook when wrapping an API that doesn't expose precise subscription events for fetched
/// data. You could then, at some point, invalidate your local cache of the fetched data and trigger
/// a re-render to let the normal render flow of components tell you again which data to fetch, and
/// repopulate the cache accordingly.
///
/// A large externally managed cache, such as a app-wide cache for GraphQL data
/// should not rerender every component whenever new data arrives, but only those where a query
Expand All @@ -56,8 +56,8 @@ impl UseForceUpdate {
/// # Example
///
/// This example implements a silly, manually updated display of the current time. The component
/// is re-rendered every time the button is clicked. You should usually use a timeout and `use_state`
/// to automatically trigger a re-render every second without having to use this hook.
/// is re-rendered every time the button is clicked. You should usually use a timeout and
/// `use_state` to automatically trigger a re-render every second without having to use this hook.
///
/// ```rust
/// use yew::prelude::*;
Expand Down
18 changes: 10 additions & 8 deletions packages/yew/src/functional/hooks/use_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ impl<T: 'static, F: FnOnce() -> T> Hook for UseMutRef<F> {
///
/// # Example
/// ```rust
/// use yew::prelude::*;
/// use web_sys::HtmlInputElement;
/// use std::rc::Rc;
/// use std::cell::RefCell;
/// use std::ops::{Deref, DerefMut};
/// use std::rc::Rc;
///
/// use web_sys::HtmlInputElement;
/// use yew::prelude::*;
///
/// #[function_component(UseRef)]
/// fn ref_hook() -> Html {
Expand Down Expand Up @@ -79,9 +80,10 @@ where
/// # Example
///
/// ```rust
/// use wasm_bindgen::{prelude::Closure, JsCast};
/// use yew::{function_component, html, use_effect_with_deps, use_node_ref, Html};
/// use wasm_bindgen::prelude::Closure;
/// use wasm_bindgen::JsCast;
/// use web_sys::{Event, HtmlElement};
/// use yew::{function_component, html, use_effect_with_deps, use_node_ref, Html};
///
/// #[function_component(UseNodeRef)]
/// pub fn node_ref_hook() -> Html {
Expand Down Expand Up @@ -128,9 +130,9 @@ where
///
/// # Tip
///
/// When conditionally rendering elements you can use `NodeRef` in conjunction with `use_effect_with_deps`
/// to perform actions each time an element is rendered and just before the component where the hook is used in is going to be removed from the
/// DOM.
/// When conditionally rendering elements you can use `NodeRef` in conjunction with
/// `use_effect_with_deps` to perform actions each time an element is rendered and just before the
/// component where the hook is used in is going to be removed from the DOM.
#[hook]
pub fn use_node_ref() -> NodeRef {
(*use_state(NodeRef::default)).clone()
Expand Down