1
0

error_boundary.py 6.1 KB

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