error_boundary.rs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. use crate::{
  2. global_context::current_scope_id, innerlude::provide_context, use_hook, Element, IntoDynNode,
  3. Properties, ScopeId, Template, TemplateAttribute, TemplateNode, VNode,
  4. };
  5. use std::{
  6. any::{Any, TypeId},
  7. backtrace::Backtrace,
  8. cell::{Ref, RefCell},
  9. error::Error,
  10. fmt::{Debug, Display},
  11. rc::Rc,
  12. str::FromStr,
  13. };
  14. /// A panic in a component that was caught by an error boundary.
  15. ///
  16. /// <div class="warning">
  17. ///
  18. /// WASM currently does not support caching unwinds, so this struct will not be created in WASM.
  19. ///
  20. /// </div>
  21. pub struct CapturedPanic {
  22. #[allow(dead_code)]
  23. /// The error that was caught
  24. pub error: Box<dyn Any + 'static>,
  25. }
  26. impl Debug for CapturedPanic {
  27. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  28. f.debug_struct("CapturedPanic").finish()
  29. }
  30. }
  31. impl Display for CapturedPanic {
  32. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  33. f.write_fmt(format_args!("Encountered panic: {:?}", self.error))
  34. }
  35. }
  36. impl Error for CapturedPanic {}
  37. /// Provide an error boundary to catch errors from child components
  38. pub fn use_error_boundary() -> ErrorContext {
  39. use_hook(|| {
  40. provide_context(ErrorContext::new(
  41. Vec::new(),
  42. current_scope_id().unwrap_or_else(|e| panic!("{}", e)),
  43. ))
  44. })
  45. }
  46. /// 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.
  47. pub trait AnyError {
  48. fn as_any(&self) -> &dyn Any;
  49. fn as_error(&self) -> &dyn Error;
  50. }
  51. /// An wrapper error type for types that only implement Display. We use a inner type here to avoid overlapping implementations for DisplayError and impl Error
  52. struct DisplayError(DisplayErrorInner);
  53. impl<E: Display + 'static> From<E> for DisplayError {
  54. fn from(e: E) -> Self {
  55. Self(DisplayErrorInner(Box::new(e)))
  56. }
  57. }
  58. struct DisplayErrorInner(Box<dyn Display>);
  59. impl Display for DisplayErrorInner {
  60. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  61. self.0.fmt(f)
  62. }
  63. }
  64. impl Debug for DisplayErrorInner {
  65. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  66. self.0.fmt(f)
  67. }
  68. }
  69. impl Error for DisplayErrorInner {}
  70. impl AnyError for DisplayError {
  71. fn as_any(&self) -> &dyn Any {
  72. &self.0 .0
  73. }
  74. fn as_error(&self) -> &dyn Error {
  75. &self.0
  76. }
  77. }
  78. /// Provides context methods to [`Result`] and [`Option`] types that are compatible with [`CapturedError`]
  79. ///
  80. /// This trait is sealed and cannot be implemented outside of dioxus-core
  81. pub trait Context<T, E>: private::Sealed {
  82. /// Add a visual representation of the error that the [`ErrorBoundary`] may render
  83. ///
  84. /// # Example
  85. /// ```rust
  86. /// # use dioxus::prelude::*;
  87. /// fn Component() -> Element {
  88. /// // You can bubble up errors with `?` inside components, and event handlers
  89. /// // Along with the error itself, you can provide a way to display the error by calling `show`
  90. /// let number = "1234".parse::<usize>().show(|error| rsx! {
  91. /// div {
  92. /// background_color: "red",
  93. /// color: "white",
  94. /// "Error parsing number: {error}"
  95. /// }
  96. /// })?;
  97. /// todo!()
  98. /// }
  99. /// ```
  100. fn show(self, display_error: impl FnOnce(&E) -> Element) -> Result<T>;
  101. /// Wrap the result additional context about the error that occurred.
  102. ///
  103. /// # Example
  104. /// ```rust
  105. /// # use dioxus::prelude::*;
  106. /// fn NumberParser() -> Element {
  107. /// // You can bubble up errors with `?` inside components, and event handlers
  108. /// // Along with the error itself, you can provide a way to display the error by calling `context`
  109. /// let number = "-1234".parse::<usize>().context("Parsing number inside of the NumberParser")?;
  110. /// todo!()
  111. /// }
  112. /// ```
  113. fn context<C: Display + 'static>(self, context: C) -> Result<T>;
  114. /// Wrap the result with additional context about the error that occurred. The closure will only be run if the Result is an error.
  115. ///
  116. /// # Example
  117. /// ```rust
  118. /// # use dioxus::prelude::*;
  119. /// fn NumberParser() -> Element {
  120. /// // You can bubble up errors with `?` inside components, and event handlers
  121. /// // Along with the error itself, you can provide a way to display the error by calling `context`
  122. /// let number = "-1234".parse::<usize>().with_context(|| format!("Timestamp: {:?}", std::time::Instant::now()))?;
  123. /// todo!()
  124. /// }
  125. /// ```
  126. fn with_context<C: Display + 'static>(self, context: impl FnOnce() -> C) -> Result<T>;
  127. }
  128. impl<T, E> Context<T, E> for std::result::Result<T, E>
  129. where
  130. E: Error + 'static,
  131. {
  132. fn show(self, display_error: impl FnOnce(&E) -> Element) -> Result<T> {
  133. // We don't use result mapping to avoid extra frames
  134. match self {
  135. std::result::Result::Ok(value) => Ok(value),
  136. Err(error) => {
  137. let render = display_error(&error).unwrap_or_default();
  138. let mut error: CapturedError = error.into();
  139. error.render = render;
  140. Err(error)
  141. }
  142. }
  143. }
  144. fn context<C: Display + 'static>(self, context: C) -> Result<T> {
  145. self.with_context(|| context)
  146. }
  147. fn with_context<C: Display + 'static>(self, context: impl FnOnce() -> C) -> Result<T> {
  148. // We don't use result mapping to avoid extra frames
  149. match self {
  150. std::result::Result::Ok(value) => Ok(value),
  151. Err(error) => {
  152. let mut error: CapturedError = error.into();
  153. error.context.push(Rc::new(AdditionalErrorContext {
  154. backtrace: Backtrace::capture(),
  155. context: Box::new(context()),
  156. scope: current_scope_id().ok(),
  157. }));
  158. Err(error)
  159. }
  160. }
  161. }
  162. }
  163. impl<T> Context<T, CapturedError> for Option<T> {
  164. fn show(self, display_error: impl FnOnce(&CapturedError) -> Element) -> Result<T> {
  165. // We don't use result mapping to avoid extra frames
  166. match self {
  167. Some(value) => Ok(value),
  168. None => {
  169. let mut error = CapturedError::from_display("Value was none");
  170. let render = display_error(&error).unwrap_or_default();
  171. error.render = render;
  172. Err(error)
  173. }
  174. }
  175. }
  176. fn context<C: Display + 'static>(self, context: C) -> Result<T> {
  177. self.with_context(|| context)
  178. }
  179. fn with_context<C: Display + 'static>(self, context: impl FnOnce() -> C) -> Result<T> {
  180. // We don't use result mapping to avoid extra frames
  181. match self {
  182. Some(value) => Ok(value),
  183. None => {
  184. let error = CapturedError::from_display(context());
  185. Err(error)
  186. }
  187. }
  188. }
  189. }
  190. pub(crate) mod private {
  191. use super::*;
  192. pub trait Sealed {}
  193. impl<T, E> Sealed for std::result::Result<T, E> where E: Error {}
  194. impl<T> Sealed for Option<T> {}
  195. }
  196. impl<T: Any + Error> AnyError for T {
  197. fn as_any(&self) -> &dyn Any {
  198. self
  199. }
  200. fn as_error(&self) -> &dyn Error {
  201. self
  202. }
  203. }
  204. /// A context with information about suspended components
  205. #[derive(Debug, Clone)]
  206. pub struct ErrorContext {
  207. errors: Rc<RefCell<Vec<CapturedError>>>,
  208. id: ScopeId,
  209. }
  210. impl PartialEq for ErrorContext {
  211. fn eq(&self, other: &Self) -> bool {
  212. Rc::ptr_eq(&self.errors, &other.errors)
  213. }
  214. }
  215. impl ErrorContext {
  216. /// Create a new suspense boundary in a specific scope
  217. pub(crate) fn new(errors: Vec<CapturedError>, id: ScopeId) -> Self {
  218. Self {
  219. errors: Rc::new(RefCell::new(errors)),
  220. id,
  221. }
  222. }
  223. /// Get all errors thrown from child components
  224. pub fn errors(&self) -> Ref<[CapturedError]> {
  225. Ref::map(self.errors.borrow(), |errors| errors.as_slice())
  226. }
  227. /// Get the Element from the first error that can be shown
  228. pub fn show(&self) -> Option<Element> {
  229. self.errors.borrow().iter().find_map(|task| task.show())
  230. }
  231. /// Push an error into this Error Boundary
  232. pub fn insert_error(&self, error: CapturedError) {
  233. self.errors.borrow_mut().push(error);
  234. self.id.needs_update();
  235. }
  236. /// Clear all errors from this Error Boundary
  237. pub fn clear_errors(&self) {
  238. self.errors.borrow_mut().clear();
  239. }
  240. }
  241. /// Errors can have additional context added as they bubble up the render tree
  242. /// This context can be used to provide additional information to the user
  243. struct AdditionalErrorContext {
  244. backtrace: Backtrace,
  245. context: Box<dyn Display>,
  246. scope: Option<ScopeId>,
  247. }
  248. impl Debug for AdditionalErrorContext {
  249. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  250. f.debug_struct("ErrorContext")
  251. .field("backtrace", &self.backtrace)
  252. .field("context", &self.context.to_string())
  253. .finish()
  254. }
  255. }
  256. impl Display for AdditionalErrorContext {
  257. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  258. let AdditionalErrorContext {
  259. backtrace,
  260. context,
  261. scope,
  262. } = self;
  263. write!(f, "{context} (from ")?;
  264. if let Some(scope) = scope {
  265. write!(f, "scope {scope:?} ")?;
  266. }
  267. write!(f, "at {backtrace:?})")
  268. }
  269. }
  270. /// A type alias for a result that can be either a boxed error or a value
  271. /// This is useful to avoid having to use `Result<T, CapturedError>` everywhere
  272. pub type Result<T = ()> = std::result::Result<T, CapturedError>;
  273. /// A helper function for an Ok result that can be either a boxed error or a value
  274. /// This is useful to avoid having to use `Ok<T, CapturedError>` everywhere
  275. #[allow(non_snake_case)]
  276. pub fn Ok<T>(value: T) -> Result<T> {
  277. Result::Ok(value)
  278. }
  279. #[derive(Clone)]
  280. /// An instance of an error captured by a descendant component.
  281. pub struct CapturedError {
  282. /// The error captured by the error boundary
  283. error: Rc<dyn AnyError + 'static>,
  284. /// The backtrace of the error
  285. backtrace: Rc<Backtrace>,
  286. /// The scope that threw the error
  287. scope: ScopeId,
  288. /// An error message that can be displayed to the user
  289. pub(crate) render: VNode,
  290. /// Additional context that was added to the error
  291. context: Vec<Rc<AdditionalErrorContext>>,
  292. }
  293. impl FromStr for CapturedError {
  294. type Err = std::convert::Infallible;
  295. fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
  296. std::result::Result::Ok(Self::from_display(s.to_string()))
  297. }
  298. }
  299. #[cfg(feature = "serialize")]
  300. #[derive(serde::Serialize, serde::Deserialize)]
  301. struct SerializedCapturedError {
  302. error: String,
  303. context: Vec<String>,
  304. }
  305. #[cfg(feature = "serialize")]
  306. impl serde::Serialize for CapturedError {
  307. fn serialize<S: serde::Serializer>(
  308. &self,
  309. serializer: S,
  310. ) -> std::result::Result<S::Ok, S::Error> {
  311. let serialized = SerializedCapturedError {
  312. error: self.error.as_error().to_string(),
  313. context: self
  314. .context
  315. .iter()
  316. .map(|context| context.to_string())
  317. .collect(),
  318. };
  319. serialized.serialize(serializer)
  320. }
  321. }
  322. #[cfg(feature = "serialize")]
  323. impl<'de> serde::Deserialize<'de> for CapturedError {
  324. fn deserialize<D: serde::Deserializer<'de>>(
  325. deserializer: D,
  326. ) -> std::result::Result<Self, D::Error> {
  327. let serialized = SerializedCapturedError::deserialize(deserializer)?;
  328. let error = DisplayError::from(serialized.error);
  329. let context = serialized
  330. .context
  331. .into_iter()
  332. .map(|context| {
  333. Rc::new(AdditionalErrorContext {
  334. scope: None,
  335. backtrace: Backtrace::disabled(),
  336. context: Box::new(context),
  337. })
  338. })
  339. .collect();
  340. std::result::Result::Ok(Self {
  341. error: Rc::new(error),
  342. context,
  343. backtrace: Rc::new(Backtrace::disabled()),
  344. scope: ScopeId::ROOT,
  345. render: VNode::placeholder(),
  346. })
  347. }
  348. }
  349. impl Debug for CapturedError {
  350. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  351. f.debug_struct("CapturedError")
  352. .field("error", &self.error.as_error())
  353. .field("backtrace", &self.backtrace)
  354. .field("scope", &self.scope)
  355. .finish()
  356. }
  357. }
  358. impl<E: AnyError + 'static> From<E> for CapturedError {
  359. fn from(error: E) -> Self {
  360. Self {
  361. error: Rc::new(error),
  362. backtrace: Rc::new(Backtrace::capture()),
  363. scope: current_scope_id()
  364. .expect("Cannot create an error boundary outside of a component's scope."),
  365. render: Default::default(),
  366. context: Default::default(),
  367. }
  368. }
  369. }
  370. impl CapturedError {
  371. /// Create a new captured error
  372. pub fn new(error: impl AnyError + 'static) -> Self {
  373. Self {
  374. error: Rc::new(error),
  375. backtrace: Rc::new(Backtrace::capture()),
  376. scope: current_scope_id().unwrap_or(ScopeId::ROOT),
  377. render: Default::default(),
  378. context: Default::default(),
  379. }
  380. }
  381. /// Create a new error from a type that only implements [`Display`]. If your type implements [`Error`], you can use [`CapturedError::from`] instead.
  382. pub fn from_display(error: impl Display + 'static) -> Self {
  383. Self {
  384. error: Rc::new(DisplayError::from(error)),
  385. backtrace: Rc::new(Backtrace::capture()),
  386. scope: current_scope_id().unwrap_or(ScopeId::ROOT),
  387. render: Default::default(),
  388. context: Default::default(),
  389. }
  390. }
  391. /// Mark the error as being thrown from a specific scope
  392. pub fn with_origin(mut self, scope: ScopeId) -> Self {
  393. self.scope = scope;
  394. self
  395. }
  396. /// Clone the error while retaining the mounted information of the error
  397. pub(crate) fn clone_mounted(&self) -> Self {
  398. Self {
  399. error: self.error.clone(),
  400. backtrace: self.backtrace.clone(),
  401. scope: self.scope,
  402. render: self.render.clone_mounted(),
  403. context: self.context.clone(),
  404. }
  405. }
  406. /// Get a VNode representation of the error if the error provides one
  407. pub fn show(&self) -> Option<Element> {
  408. if self.render == VNode::placeholder() {
  409. None
  410. } else {
  411. Some(std::result::Result::Ok(self.render.clone()))
  412. }
  413. }
  414. }
  415. impl PartialEq for CapturedError {
  416. fn eq(&self, other: &Self) -> bool {
  417. format!("{:?}", self) == format!("{:?}", other)
  418. }
  419. }
  420. impl Display for CapturedError {
  421. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  422. f.write_fmt(format_args!(
  423. "Encountered error: {:?}\nIn scope: {:?}\nBacktrace: {}\nContext: ",
  424. self.error.as_error(),
  425. self.scope,
  426. self.backtrace
  427. ))?;
  428. for context in &*self.context {
  429. f.write_fmt(format_args!("{}\n", context))?;
  430. }
  431. std::result::Result::Ok(())
  432. }
  433. }
  434. impl CapturedError {
  435. /// Downcast the error type into a concrete error type
  436. pub fn downcast<T: 'static>(&self) -> Option<&T> {
  437. if TypeId::of::<T>() == (*self.error).type_id() {
  438. self.error.as_any().downcast_ref::<T>()
  439. } else {
  440. None
  441. }
  442. }
  443. }
  444. pub(crate) fn throw_into(error: impl Into<CapturedError>, scope: ScopeId) {
  445. let error = error.into();
  446. if let Some(cx) = scope.consume_context::<ErrorContext>() {
  447. cx.insert_error(error)
  448. } else {
  449. tracing::error!(
  450. "Tried to throw an error into an error boundary, but failed to locate a boundary: {:?}",
  451. error
  452. )
  453. }
  454. }
  455. #[allow(clippy::type_complexity)]
  456. #[derive(Clone)]
  457. pub struct ErrorHandler(Rc<dyn Fn(ErrorContext) -> Element>);
  458. impl<F: Fn(ErrorContext) -> Element + 'static> From<F> for ErrorHandler {
  459. fn from(value: F) -> Self {
  460. Self(Rc::new(value))
  461. }
  462. }
  463. fn default_handler(errors: ErrorContext) -> Element {
  464. static TEMPLATE: Template = Template {
  465. name: "error_handle.rs:42:5:884",
  466. roots: &[TemplateNode::Element {
  467. tag: "div",
  468. namespace: None,
  469. attrs: &[TemplateAttribute::Static {
  470. name: "color",
  471. namespace: Some("style"),
  472. value: "red",
  473. }],
  474. children: &[TemplateNode::Dynamic { id: 0usize }],
  475. }],
  476. node_paths: &[&[0u8, 0u8]],
  477. attr_paths: &[],
  478. };
  479. std::result::Result::Ok(VNode::new(
  480. None,
  481. TEMPLATE,
  482. Box::new([errors
  483. .errors()
  484. .iter()
  485. .map(|e| {
  486. static TEMPLATE: Template = Template {
  487. name: "error_handle.rs:43:5:884",
  488. roots: &[TemplateNode::Element {
  489. tag: "pre",
  490. namespace: None,
  491. attrs: &[],
  492. children: &[TemplateNode::Dynamic { id: 0usize }],
  493. }],
  494. node_paths: &[&[0u8, 0u8]],
  495. attr_paths: &[],
  496. };
  497. VNode::new(
  498. None,
  499. TEMPLATE,
  500. Box::new([e.to_string().into_dyn_node()]),
  501. Default::default(),
  502. )
  503. })
  504. .into_dyn_node()]),
  505. Default::default(),
  506. ))
  507. }
  508. #[derive(Clone)]
  509. pub struct ErrorBoundaryProps {
  510. children: Element,
  511. handle_error: ErrorHandler,
  512. }
  513. impl ErrorBoundaryProps {
  514. /**
  515. Create a builder for building `ErrorBoundaryProps`.
  516. On the builder, call `.children(...)`(optional), `.handle_error(...)`(optional) to set the values of the fields.
  517. Finally, call `.build()` to create the instance of `ErrorBoundaryProps`.
  518. */
  519. #[allow(dead_code)]
  520. pub fn builder() -> ErrorBoundaryPropsBuilder<((), ())> {
  521. ErrorBoundaryPropsBuilder { fields: ((), ()) }
  522. }
  523. }
  524. #[must_use]
  525. #[doc(hidden)]
  526. #[allow(dead_code, non_camel_case_types, non_snake_case)]
  527. pub struct ErrorBoundaryPropsBuilder<TypedBuilderFields> {
  528. fields: TypedBuilderFields,
  529. }
  530. impl<TypedBuilderFields> Clone for ErrorBoundaryPropsBuilder<TypedBuilderFields>
  531. where
  532. TypedBuilderFields: Clone,
  533. {
  534. fn clone(&self) -> Self {
  535. Self {
  536. fields: self.fields.clone(),
  537. }
  538. }
  539. }
  540. impl Properties for ErrorBoundaryProps {
  541. type Builder = ErrorBoundaryPropsBuilder<((), ())>;
  542. fn builder() -> Self::Builder {
  543. ErrorBoundaryProps::builder()
  544. }
  545. fn memoize(&mut self, other: &Self) -> bool {
  546. *self = other.clone();
  547. false
  548. }
  549. }
  550. #[doc(hidden)]
  551. #[allow(dead_code, non_camel_case_types, non_snake_case)]
  552. pub trait ErrorBoundaryPropsBuilder_Optional<T> {
  553. fn into_value<F: FnOnce() -> T>(self, default: F) -> T;
  554. }
  555. impl<T> ErrorBoundaryPropsBuilder_Optional<T> for () {
  556. fn into_value<F: FnOnce() -> T>(self, default: F) -> T {
  557. default()
  558. }
  559. }
  560. impl<T> ErrorBoundaryPropsBuilder_Optional<T> for (T,) {
  561. fn into_value<F: FnOnce() -> T>(self, _: F) -> T {
  562. self.0
  563. }
  564. }
  565. #[allow(dead_code, non_camel_case_types, missing_docs)]
  566. impl<__handle_error> ErrorBoundaryPropsBuilder<((), __handle_error)> {
  567. pub fn children(
  568. self,
  569. children: Element,
  570. ) -> ErrorBoundaryPropsBuilder<((Element,), __handle_error)> {
  571. let children = (children,);
  572. let (_, handle_error) = self.fields;
  573. ErrorBoundaryPropsBuilder {
  574. fields: (children, handle_error),
  575. }
  576. }
  577. }
  578. #[doc(hidden)]
  579. #[allow(dead_code, non_camel_case_types, non_snake_case)]
  580. pub enum ErrorBoundaryPropsBuilder_Error_Repeated_field_children {}
  581. #[doc(hidden)]
  582. #[allow(dead_code, non_camel_case_types, missing_docs)]
  583. impl<__handle_error> ErrorBoundaryPropsBuilder<((Element,), __handle_error)> {
  584. #[deprecated(note = "Repeated field children")]
  585. pub fn children(
  586. self,
  587. _: ErrorBoundaryPropsBuilder_Error_Repeated_field_children,
  588. ) -> ErrorBoundaryPropsBuilder<((Element,), __handle_error)> {
  589. self
  590. }
  591. }
  592. #[allow(dead_code, non_camel_case_types, missing_docs)]
  593. impl<__children> ErrorBoundaryPropsBuilder<(__children, ())> {
  594. pub fn handle_error(
  595. self,
  596. handle_error: impl ::core::convert::Into<ErrorHandler>,
  597. ) -> ErrorBoundaryPropsBuilder<(__children, (ErrorHandler,))> {
  598. let handle_error = (handle_error.into(),);
  599. let (children, _) = self.fields;
  600. ErrorBoundaryPropsBuilder {
  601. fields: (children, handle_error),
  602. }
  603. }
  604. }
  605. #[doc(hidden)]
  606. #[allow(dead_code, non_camel_case_types, non_snake_case)]
  607. pub enum ErrorBoundaryPropsBuilder_Error_Repeated_field_handle_error {}
  608. #[doc(hidden)]
  609. #[allow(dead_code, non_camel_case_types, missing_docs)]
  610. impl<__children> ErrorBoundaryPropsBuilder<(__children, (ErrorHandler,))> {
  611. #[deprecated(note = "Repeated field handle_error")]
  612. pub fn handle_error(
  613. self,
  614. _: ErrorBoundaryPropsBuilder_Error_Repeated_field_handle_error,
  615. ) -> ErrorBoundaryPropsBuilder<(__children, (ErrorHandler,))> {
  616. self
  617. }
  618. }
  619. #[allow(dead_code, non_camel_case_types, missing_docs)]
  620. impl<
  621. __handle_error: ErrorBoundaryPropsBuilder_Optional<ErrorHandler>,
  622. __children: ErrorBoundaryPropsBuilder_Optional<Element>,
  623. > ErrorBoundaryPropsBuilder<(__children, __handle_error)>
  624. {
  625. pub fn build(self) -> ErrorBoundaryProps {
  626. let (children, handle_error) = self.fields;
  627. let children = ErrorBoundaryPropsBuilder_Optional::into_value(children, VNode::empty);
  628. let handle_error = ErrorBoundaryPropsBuilder_Optional::into_value(handle_error, || {
  629. ErrorHandler(Rc::new(default_handler))
  630. });
  631. ErrorBoundaryProps {
  632. children,
  633. handle_error,
  634. }
  635. }
  636. }
  637. /// Create a new error boundary component that catches any errors thrown from child components
  638. ///
  639. /// ## Details
  640. ///
  641. /// Error boundaries handle errors within a specific part of your application. Any errors passed up from a child will be caught by the nearest error boundary.
  642. ///
  643. /// ## Example
  644. ///
  645. /// ```rust, no_run
  646. /// # use dioxus::prelude::*;
  647. /// fn App() -> Element {
  648. /// rsx! {
  649. /// ErrorBoundary {
  650. /// handle_error: |errors: ErrorContext| rsx! { "Oops, we encountered an error. Please report {errors:?} to the developer of this application" },
  651. /// Counter {
  652. /// multiplier: "1234"
  653. /// }
  654. /// }
  655. /// }
  656. /// }
  657. ///
  658. /// #[component]
  659. /// fn Counter(multiplier: String) -> Element {
  660. /// // You can bubble up errors with `?` inside components
  661. /// let multiplier_parsed = multiplier.parse::<usize>()?;
  662. /// let mut count = use_signal(|| multiplier_parsed);
  663. /// rsx! {
  664. /// button {
  665. /// // Or inside event handlers
  666. /// onclick: move |_| {
  667. /// let multiplier_parsed = multiplier.parse::<usize>()?;
  668. /// *count.write() *= multiplier_parsed;
  669. /// Ok(())
  670. /// },
  671. /// "{count}x{multiplier}"
  672. /// }
  673. /// }
  674. /// }
  675. /// ```
  676. ///
  677. /// ## Usage
  678. ///
  679. /// Error boundaries are an easy way to handle errors in your application.
  680. /// They are similar to `try/catch` in JavaScript, but they only catch errors in the tree below them.
  681. /// 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.
  682. #[allow(non_upper_case_globals, non_snake_case)]
  683. pub fn ErrorBoundary(props: ErrorBoundaryProps) -> Element {
  684. let error_boundary = use_error_boundary();
  685. let errors = error_boundary.errors();
  686. if errors.is_empty() {
  687. std::result::Result::Ok({
  688. static TEMPLATE: Template = Template {
  689. name: "examples/error_handle.rs:81:17:2342",
  690. roots: &[TemplateNode::Dynamic { id: 0usize }],
  691. node_paths: &[&[0u8]],
  692. attr_paths: &[],
  693. };
  694. VNode::new(
  695. None,
  696. TEMPLATE,
  697. Box::new([(props.children).into_dyn_node()]),
  698. Default::default(),
  699. )
  700. })
  701. } else {
  702. (props.handle_error.0)(error_boundary.clone())
  703. }
  704. }