Skip to content

Commit

Permalink
Resume Suspension upon unmount (#2874)
Browse files Browse the repository at this point in the history
* Restore a behaviour of destroy upon unmount.

* Add a test.

* Fix CI.

* Fix CI.
  • Loading branch information
futursolo committed Sep 15, 2022
1 parent beb0157 commit ce70e4c
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 9 deletions.
21 changes: 13 additions & 8 deletions packages/yew/src/html/component/lifecycle.rs
Expand Up @@ -297,6 +297,17 @@ impl ComponentState {
.downcast_ref::<CompStateInner<COMP>>()
.map(|m| &m.component)
}

fn resume_existing_suspension(&mut self) {
if let Some(m) = self.suspension.take() {
let comp_scope = self.inner.any_scope();

let suspense_scope = comp_scope.find_parent_scope::<BaseSuspense>().unwrap();
let suspense = suspense_scope.get_component().unwrap();

suspense.resume(m);
}
}
}

pub(crate) struct CreateRunner<COMP: BaseComponent> {
Expand Down Expand Up @@ -370,6 +381,7 @@ impl ComponentState {
)]
fn destroy(mut self, parent_to_detach: bool) {
self.inner.destroy();
self.resume_existing_suspension();

match self.render_state {
#[cfg(feature = "csr")]
Expand Down Expand Up @@ -479,14 +491,7 @@ impl ComponentState {
fn commit_render(&mut self, shared_state: &Shared<Option<ComponentState>>, new_root: Html) {
// Currently not suspended, we remove any previous suspension and update
// normally.
if let Some(m) = self.suspension.take() {
let comp_scope = self.inner.any_scope();

let suspense_scope = comp_scope.find_parent_scope::<BaseSuspense>().unwrap();
let suspense = suspense_scope.get_component().unwrap();

suspense.resume(m);
}
self.resume_existing_suspension();

match self.render_state {
#[cfg(feature = "csr")]
Expand Down
52 changes: 51 additions & 1 deletion packages/yew/tests/suspense.rs
Expand Up @@ -8,9 +8,9 @@ use std::time::Duration;

use common::obtain_result;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::spawn_local;
use wasm_bindgen_test::*;
use web_sys::{HtmlElement, HtmlTextAreaElement};
use yew::platform::spawn_local;
use yew::platform::time::sleep;
use yew::prelude::*;
use yew::suspense::{use_future, use_future_with_deps, Suspension, SuspensionResult};
Expand Down Expand Up @@ -684,3 +684,53 @@ async fn use_suspending_future_with_deps_works() {
let result = obtain_result();
assert_eq!(result.as_str(), r#"<div>42</div>"#);
}

#[wasm_bindgen_test]
async fn test_suspend_forever() {
/// A component that its suspension never resumes.
/// We test that this can be used with to trigger a suspension and unsuspend upon unmount.
#[function_component]
fn SuspendForever() -> HtmlResult {
let (s, handle) = Suspension::new();
use_state(move || handle);
Err(s.into())
}

#[function_component]
fn App() -> Html {
let page = use_state(|| 1);

{
let page_setter = page.setter();
use_effect_with_deps(
move |_| {
spawn_local(async move {
sleep(Duration::from_secs(1)).await;
page_setter.set(2);
});
},
(),
);
}

let content = if *page == 1 {
html! { <SuspendForever /> }
} else {
html! { <div id="result">{"OK"}</div> }
};

html! {
<Suspense fallback={html! {<div>{"Loading..."}</div>}}>
{content}
</Suspense>
}
}

yew::Renderer::<App>::with_root(gloo::utils::document().get_element_by_id("output").unwrap())
.render();

sleep(Duration::from_millis(1500)).await;

let result = obtain_result();
assert_eq!(result.as_str(), r#"OK"#);
}

0 comments on commit ce70e4c

Please sign in to comment.