Resume Suspension upon unmount (#2874)

* Restore a behaviour of destroy upon unmount.

* Add a test.

* Fix CI.

* Fix CI.
This commit is contained in:
Kaede Hoshikawa 2022-09-15 22:26:45 +09:00 committed by GitHub
parent beb0157c48
commit ce70e4c1fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 64 additions and 9 deletions

View File

@ -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> {
@ -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<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")]

View File

@ -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#"<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"#);
}