Bubble errors from the server to the client on suspense boundaries (#2772)

* Bubble errors from the server to the client on suspense boundaries

* make resolving suspense boundaries O(n)
This commit is contained in:
Evan Almloff authored and GitHub committed 2024-08-02 19:01:55 -07:00
1 parent 3003362b52
commit b0a2e2418c
12 files changed
+190 -39

No files matched your search

+8 -8
View File
@@ -40,13 +40,11 @@ impl Display for CapturedPanic {
impl Error for CapturedPanic {}
/// Provide an error boundary to catch errors from child components
pub fn use_error_boundary() -> ErrorContext {
use_hook(|| {
provide_context(ErrorContext::new(
Vec::new(),
current_scope_id().unwrap_or_else(|e| panic!("{}", e)),
))
})
pub fn provide_error_boundary() -> ErrorContext {
provide_context(ErrorContext::new(
Vec::new(),
current_scope_id().unwrap_or_else(|e| panic!("{}", e)),
))
}
/// A trait for any type that can be downcast to a concrete type and implements Debug. This is automatically implemented for all types that implement Any + Debug.
@@ -750,7 +748,7 @@ impl<
/// Error boundaries are quick to implement, but it can be useful to individually handle errors in your components to provide a better user experience when you know that an error is likely to occur.
#[allow(non_upper_case_globals, non_snake_case)]
pub fn ErrorBoundary(props: ErrorBoundaryProps) -> Element {
let error_boundary = use_error_boundary();
let error_boundary = use_hook(provide_error_boundary);
let errors = error_boundary.errors();
if errors.is_empty() {
std::result::Result::Ok({
@@ -768,6 +766,8 @@ pub fn ErrorBoundary(props: ErrorBoundaryProps) -> Element {
)
})
} else {
tracing::trace!("scope id: {:?}", current_scope_id());
tracing::trace!("handling errors: {:?}", errors);
(props.handle_error.0)(error_boundary.clone())
}
}
+4 -4
View File
@@ -88,10 +88,10 @@ pub mod prelude {
pub use crate::innerlude::{
consume_context, consume_context_from_scope, current_owner, current_scope_id,
fc_to_builder, generation, has_context, needs_update, needs_update_any, parent_scope,
provide_context, provide_root_context, queue_effect, remove_future, schedule_update,
schedule_update_any, spawn, spawn_forever, spawn_isomorphic, suspend, throw_error,
try_consume_context, use_after_render, use_before_render, use_drop, use_error_boundary,
use_hook, use_hook_with_cleanup, with_owner, AnyValue, Attribute, Callback, Component,
provide_context, provide_error_boundary, provide_root_context, queue_effect, remove_future,
schedule_update, schedule_update_any, spawn, spawn_forever, spawn_isomorphic, suspend,
throw_error, try_consume_context, use_after_render, use_before_render, use_drop, use_hook,
use_hook_with_cleanup, with_owner, AnyValue, Attribute, Callback, Component,
ComponentFunction, Context, Element, ErrorBoundary, ErrorContext, Event, EventHandler,
Fragment, HasAttributes, IntoAttributeValue, IntoDynNode, OptionStringFromMarker,
Properties, ReactiveContext, RenderError, RenderReturn, Runtime, RuntimeGuard, ScopeId,
+6 -6
View File
@@ -190,10 +190,10 @@ impl Scope {
/// Clones the state if it exists.
pub fn consume_context<T: 'static + Clone>(&self) -> Option<T> {
tracing::trace!(
"looking for context {} ({:?}) in {}",
"looking for context {} ({:?}) in {:?}",
std::any::type_name::<T>(),
std::any::TypeId::of::<T>(),
self.name
self.id
);
if let Some(this_ctx) = self.has_context() {
return Some(this_ctx);
@@ -207,10 +207,10 @@ impl Scope {
return None;
};
tracing::trace!(
"looking for context {} ({:?}) in {}",
"looking for context {} ({:?}) in {:?}",
std::any::type_name::<T>(),
std::any::TypeId::of::<T>(),
parent.name
parent.id
);
if let Some(shared) = parent.has_context() {
return Some(shared);
@@ -277,10 +277,10 @@ impl Scope {
/// ```
pub fn provide_context<T: 'static + Clone>(&self, value: T) -> T {
tracing::trace!(
"providing context {} ({:?}) in {}",
"providing context {} ({:?}) in {:?}",
std::any::type_name::<T>(),
std::any::TypeId::of::<T>(),
self.name
self.id
);
let mut contexts = self.shared_contexts.borrow_mut();