diff --git a/packages/yew/src/html/component/lifecycle.rs b/packages/yew/src/html/component/lifecycle.rs index 9bde886f4d4..fca8ee27b7c 100644 --- a/packages/yew/src/html/component/lifecycle.rs +++ b/packages/yew/src/html/component/lifecycle.rs @@ -297,6 +297,17 @@ impl ComponentState { .downcast_ref::>() .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::().unwrap(); + let suspense = suspense_scope.get_component().unwrap(); + + suspense.resume(m); + } + } } pub(crate) struct CreateRunner { @@ -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")] @@ -479,14 +491,7 @@ impl ComponentState { fn commit_render(&mut self, shared_state: &Shared>, 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::().unwrap(); - let suspense = suspense_scope.get_component().unwrap(); - - suspense.resume(m); - } + self.resume_existing_suspension(); match self.render_state { #[cfg(feature = "csr")] diff --git a/packages/yew/tests/suspense.rs b/packages/yew/tests/suspense.rs index 969cc5fcbf6..0183413f796 100644 --- a/packages/yew/tests/suspense.rs +++ b/packages/yew/tests/suspense.rs @@ -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}; @@ -684,3 +684,53 @@ async fn use_suspending_future_with_deps_works() { let result = obtain_result(); assert_eq!(result.as_str(), r#"
42
"#); } + +#[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! { } + } else { + html! {
{"OK"}
} + }; + + html! { + {"Loading..."}}}> + {content} + + } + } + + yew::Renderer::::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"#); +}