error_boundary.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. """A React Error Boundary component that catches unhandled frontend exceptions."""
  2. from __future__ import annotations
  3. from typing import Dict, Tuple
  4. from reflex.components.component import Component
  5. from reflex.components.datadisplay.logo import svg_logo
  6. from reflex.components.el import a, button, details, div, h2, hr, p, pre, summary
  7. from reflex.event import EventHandler, set_clipboard
  8. from reflex.state import FrontendEventExceptionState
  9. from reflex.vars.base import Var
  10. from reflex.vars.function import ArgsFunctionOperation
  11. from reflex.vars.object import ObjectVar
  12. def on_error_spec(
  13. error: ObjectVar[Dict[str, str]], info: ObjectVar[Dict[str, str]]
  14. ) -> Tuple[Var[str], Var[str]]:
  15. """The spec for the on_error event handler.
  16. Args:
  17. error: The error message.
  18. info: Additional information about the error.
  19. Returns:
  20. The arguments for the event handler.
  21. """
  22. return (
  23. error.stack,
  24. info.componentStack,
  25. )
  26. class ErrorBoundary(Component):
  27. """A React Error Boundary component that catches unhandled frontend exceptions."""
  28. library = "react-error-boundary"
  29. tag = "ErrorBoundary"
  30. # Fired when the boundary catches an error.
  31. on_error: EventHandler[on_error_spec]
  32. # Rendered instead of the children when an error is caught.
  33. fallback_render: Var[Component]
  34. @classmethod
  35. def create(cls, *children, **props):
  36. """Create an ErrorBoundary component.
  37. Args:
  38. *children: The children of the component.
  39. **props: The props of the component.
  40. Returns:
  41. The ErrorBoundary component.
  42. """
  43. if "on_error" not in props:
  44. props["on_error"] = FrontendEventExceptionState.handle_frontend_exception
  45. if "fallback_render" not in props:
  46. props["fallback_render"] = ArgsFunctionOperation.create(
  47. ("event_args",),
  48. Var.create(
  49. div(
  50. div(
  51. div(
  52. h2(
  53. "An error occurred while rendering this page.",
  54. font_size="1.25rem",
  55. font_weight="bold",
  56. ),
  57. p(
  58. "This is an error with the application itself.",
  59. opacity="0.75",
  60. ),
  61. details(
  62. summary("Error message", padding="0.5rem"),
  63. div(
  64. div(
  65. pre(
  66. Var(
  67. _js_expr="event_args.error.stack",
  68. ),
  69. ),
  70. padding="0.5rem",
  71. width="fit-content",
  72. ),
  73. width="100%",
  74. max_height="50vh",
  75. overflow="auto",
  76. background="#000",
  77. color="#fff",
  78. border_radius="0.25rem",
  79. ),
  80. button(
  81. "Copy",
  82. on_click=set_clipboard(
  83. Var(_js_expr="event_args.error.stack"),
  84. ),
  85. padding="0.35rem 0.75rem",
  86. margin="0.5rem",
  87. background="#fff",
  88. color="#000",
  89. border="1px solid #000",
  90. border_radius="0.25rem",
  91. font_weight="bold",
  92. ),
  93. ),
  94. display="flex",
  95. flex_direction="column",
  96. gap="1rem",
  97. max_width="50ch",
  98. border="1px solid #888888",
  99. border_radius="0.25rem",
  100. padding="1rem",
  101. ),
  102. hr(
  103. border_color="currentColor",
  104. opacity="0.25",
  105. ),
  106. a(
  107. div(
  108. "Built with ",
  109. svg_logo("currentColor"),
  110. display="flex",
  111. align_items="baseline",
  112. justify_content="center",
  113. font_family="monospace",
  114. gap="0.5rem",
  115. ),
  116. href="https://reflex.dev",
  117. ),
  118. display="flex",
  119. flex_direction="column",
  120. gap="1rem",
  121. ),
  122. height="100%",
  123. width="100%",
  124. position="absolute",
  125. display="flex",
  126. align_items="center",
  127. justify_content="center",
  128. )
  129. ),
  130. _var_type=Component,
  131. )
  132. else:
  133. props["fallback_render"] = ArgsFunctionOperation.create(
  134. ("event_args",),
  135. props["fallback_render"],
  136. _var_type=Component,
  137. )
  138. return super().create(*children, **props)
  139. error_boundary = ErrorBoundary.create