fix: Ok => Err transition with Suspend (closes #2957)

This commit is contained in:
Greg Johnston 2024-09-16 21:02:30 -04:00
parent 1fce8931ab
commit 66cf21f650
2 changed files with 36 additions and 11 deletions

View file

@ -4,7 +4,7 @@ use leptos_macro::component;
use reactive_graph::{
computed::ArcMemo,
effect::RenderEffect,
owner::Owner,
owner::{provide_context, Owner},
signal::ArcRwSignal,
traits::{Get, Update, With, WithUntracked},
};
@ -13,6 +13,7 @@ use std::{fmt::Debug, marker::PhantomData, sync::Arc};
use tachys::{
html::attribute::Attribute,
hydration::Cursor,
reactive_graph::OwnedView,
renderer::Renderer,
ssr::StreamBuilder,
view::{
@ -96,17 +97,25 @@ where
let hook = hook as Arc<dyn ErrorHook>;
let _guard = throw_error::set_error_hook(Arc::clone(&hook));
let children = children.into_inner()();
ErrorBoundaryView {
hook,
boundary_id,
errors_empty,
children,
errors,
fallback,
rndr: PhantomData,
}
let owner = Owner::new();
let children = owner.with(|| {
provide_context(Arc::clone(&hook));
children.into_inner()()
});
OwnedView::new_with_owner(
ErrorBoundaryView {
hook,
boundary_id,
errors_empty,
children,
errors,
fallback,
rndr: PhantomData,
},
owner,
)
}
struct ErrorBoundaryView<Chil, FalFn, Rndr> {

View file

@ -31,6 +31,7 @@ use std::{
rc::Rc,
sync::{Arc, Mutex, Weak},
};
use throw_error::ErrorHook;
/// A suspended `Future`, which can be used in the view.
#[derive(Clone)]
@ -177,6 +178,7 @@ where
// get a unique ID if there's a SuspenseContext
let id = use_context::<SuspenseContext>().map(|sc| sc.task_id());
let error_hook = use_context::<Arc<dyn ErrorHook>>();
// if the initial state was pending, spawn a future to wait for it
// spawning immediately means that our now_or_never poll result isn't lost
@ -185,6 +187,10 @@ where
Executor::spawn_local({
let state = Rc::clone(&inner);
async move {
let _guard = error_hook.as_ref().map(|hook| {
throw_error::set_error_hook(Arc::clone(hook))
});
let value = fut.as_mut().await;
drop(id);
Some(value).rebuild(&mut *state.borrow_mut());
@ -203,11 +209,16 @@ where
// get a unique ID if there's a SuspenseContext
let fut = inner;
let id = use_context::<SuspenseContext>().map(|sc| sc.task_id());
let error_hook = use_context::<Arc<dyn ErrorHook>>();
// spawn the future, and rebuild the state when it resolves
Executor::spawn_local({
let state = Rc::clone(&state.inner);
async move {
let _guard = error_hook
.as_ref()
.map(|hook| throw_error::set_error_hook(Arc::clone(hook)));
let value = fut.await;
drop(id);
// waiting a tick here allows Suspense to remount if necessary, which prevents some
@ -368,6 +379,7 @@ where
// get a unique ID if there's a SuspenseContext
let id = use_context::<SuspenseContext>().map(|sc| sc.task_id());
let error_hook = use_context::<Arc<dyn ErrorHook>>();
// if the initial state was pending, spawn a future to wait for it
// spawning immediately means that our now_or_never poll result isn't lost
@ -376,6 +388,10 @@ where
Executor::spawn_local({
let state = Rc::clone(&inner);
async move {
let _guard = error_hook.as_ref().map(|hook| {
throw_error::set_error_hook(Arc::clone(hook))
});
let value = fut.as_mut().await;
drop(id);
Some(value).rebuild(&mut *state.borrow_mut());