diff --git a/examples/error_handle.rs b/examples/error_handle.rs index ccafe0642..04f6c3761 100644 --- a/examples/error_handle.rs +++ b/examples/error_handle.rs @@ -20,6 +20,14 @@ fn app() -> Element { }, DemoC { x: 1 } } + + ErrorBoundary { + handle_error: |error: CapturedError| rsx! { + h1 { "Another error occurred" } + pre { "{error:#?}" } + }, + ComponentPanic {} + } } } @@ -40,3 +48,7 @@ fn DemoC(x: i32) -> Element { } } } + +fn ComponentPanic() -> Element { + panic!("This component panics") +} diff --git a/packages/core/src/any_props.rs b/packages/core/src/any_props.rs index b737a549a..760b821dc 100644 --- a/packages/core/src/any_props.rs +++ b/packages/core/src/any_props.rs @@ -1,4 +1,8 @@ -use crate::{nodes::RenderReturn, ComponentFunction}; +use crate::{ + innerlude::{throw_error, CapturedPanic}, + nodes::RenderReturn, + ComponentFunction, +}; use std::{any::Any, panic::AssertUnwindSafe}; pub(crate) type BoxedAnyProps = Box; @@ -79,6 +83,7 @@ impl + Clone, P: Clone + 'static, M: 'static> AnyProp Err(err) => { let component_name = self.name; tracing::error!("Error while rendering component `{component_name}`: {err:?}"); + throw_error::<()>(CapturedPanic { error: err }); RenderReturn::default() } } diff --git a/packages/core/src/error_boundary.rs b/packages/core/src/error_boundary.rs index 6e1055c6e..88359dd1c 100644 --- a/packages/core/src/error_boundary.rs +++ b/packages/core/src/error_boundary.rs @@ -13,6 +13,20 @@ use std::{ rc::Rc, }; +/// A panic in a component that was caught by an error boundary. +/// +/// NOTE: WASM currently does not support caching unwinds, so this struct will not be created in WASM. +pub struct CapturedPanic { + /// The error that was caught + pub error: Box, +} + +impl Debug for CapturedPanic { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CapturedPanic").finish() + } +} + /// Provide an error boundary to catch errors from child components pub fn use_error_boundary() -> ErrorBoundary { use_hook(|| provide_context(ErrorBoundary::new())) @@ -201,7 +215,7 @@ pub trait Throw: Sized { } } -fn throw_error(e: impl Debug + 'static) -> Option { +pub(crate) fn throw_error(e: impl Debug + 'static) -> Option { if let Some(cx) = try_consume_context::() { match current_scope_id() { Some(id) => cx.insert_error(id, Box::new(e), Backtrace::capture()),