diff --git a/reflex/components/base/error_boundary.py b/reflex/components/base/error_boundary.py index b8b4ef3a6..b35c69a60 100644 --- a/reflex/components/base/error_boundary.py +++ b/reflex/components/base/error_boundary.py @@ -7,14 +7,12 @@ from typing import Dict, List, Tuple from reflex.compiler.compiler import _compile_component from reflex.components.component import Component from reflex.components.el import div, p -from reflex.constants import Hooks, Imports -from reflex.event import EventChain, EventHandler -from reflex.utils.imports import ImportVar +from reflex.event import EventHandler +from reflex.state import FrontendEventExceptionState from reflex.vars.base import Var -from reflex.vars.function import FunctionVar -def on_error_spec(error: Var, info: Var[Dict[str, str]]) -> Tuple[Var[str]]: +def on_error_spec(error: Var, info: Var[Dict[str, str]]) -> Tuple[Var[str], Var[str]]: """The spec for the on_error event handler. Args: @@ -24,10 +22,10 @@ def on_error_spec(error: Var, info: Var[Dict[str, str]]) -> Tuple[Var[str]]: Returns: The arguments for the event handler. """ - return (info.componentStack,) - - -LOG_FRONTEND_ERROR = Var("logFrontendError").to(FunctionVar, EventChain) + return ( + error.stack, + info.componentStack, + ) class ErrorBoundary(Component): @@ -37,33 +35,13 @@ class ErrorBoundary(Component): tag = "ErrorBoundary" # Fired when the boundary catches an error. - on_error: EventHandler[on_error_spec] = LOG_FRONTEND_ERROR # type: ignore + on_error: EventHandler[on_error_spec] # Rendered instead of the children when an error is caught. Fallback_component: Var[Component] = Var(_js_expr="Fallback")._replace( _var_type=Component ) - def add_imports(self) -> dict[str, list[ImportVar]]: - """Add imports for the component. - - Returns: - The imports to add. - """ - return Imports.EVENTS - - def add_hooks(self) -> List[str | Var]: - """Add hooks for the component. - - Returns: - The hooks to add. - """ - return ( - [Hooks.EVENTS, Hooks.FRONTEND_ERRORS] - if "on_error" not in self.event_triggers - else [] - ) - def add_custom_code(self) -> List[str]: """Add custom Javascript code into the page that contains this component. @@ -93,5 +71,20 @@ class ErrorBoundary(Component): """ ] + @classmethod + def create(cls, *children, **props): + """Create an ErrorBoundary component. + + Args: + *children: The children of the component. + **props: The props of the component. + + Returns: + The ErrorBoundary component. + """ + if "on_error" not in props: + props["on_error"] = FrontendEventExceptionState.handle_frontend_exception + return super().create(*children, **props) + error_boundary = ErrorBoundary.create diff --git a/reflex/components/base/error_boundary.pyi b/reflex/components/base/error_boundary.pyi index 80b6880ba..94b129bdc 100644 --- a/reflex/components/base/error_boundary.pyi +++ b/reflex/components/base/error_boundary.pyi @@ -6,19 +6,15 @@ from typing import Any, Dict, List, Optional, Tuple, Union, overload from reflex.components.component import Component -from reflex.event import EventChain, EventType +from reflex.event import EventType from reflex.style import Style -from reflex.utils.imports import ImportVar from reflex.vars.base import Var -from reflex.vars.function import FunctionVar -def on_error_spec(error: Var, info: Var[Dict[str, str]]) -> Tuple[Var[str]]: ... - -LOG_FRONTEND_ERROR = Var("logFrontendError").to(FunctionVar, EventChain) +def on_error_spec( + error: Var, info: Var[Dict[str, str]] +) -> Tuple[Var[str], Var[str]]: ... class ErrorBoundary(Component): - def add_imports(self) -> dict[str, list[ImportVar]]: ... - def add_hooks(self) -> List[str | Var]: ... def add_custom_code(self) -> List[str]: ... @overload @classmethod @@ -36,7 +32,7 @@ class ErrorBoundary(Component): on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, - on_error: Optional[EventType[str]] = None, + on_error: Optional[EventType[str, str]] = None, on_focus: Optional[EventType[[]]] = None, on_mount: Optional[EventType[[]]] = None, on_mouse_down: Optional[EventType[[]]] = None, @@ -50,7 +46,7 @@ class ErrorBoundary(Component): on_unmount: Optional[EventType[[]]] = None, **props, ) -> "ErrorBoundary": - """Create the component. + """Create an ErrorBoundary component. Args: *children: The children of the component. @@ -64,7 +60,7 @@ class ErrorBoundary(Component): **props: The props of the component. Returns: - The component. + The ErrorBoundary component. """ ... diff --git a/reflex/constants/compiler.py b/reflex/constants/compiler.py index 557a92092..318a93783 100644 --- a/reflex/constants/compiler.py +++ b/reflex/constants/compiler.py @@ -132,16 +132,6 @@ class Hooks(SimpleNamespace): } })""" - FRONTEND_ERRORS = f""" - const logFrontendError = (error, info) => {{ - if (process.env.NODE_ENV === "production") {{ - addEvents([Event("{CompileVars.FRONTEND_EXCEPTION_STATE_FULL}.handle_frontend_exception", {{ - stack: error.stack, - }})]) - }} - }} - """ - class MemoizationDisposition(enum.Enum): """The conditions under which a component should be memoized.""" diff --git a/reflex/state.py b/reflex/state.py index 7b338b4d6..502162830 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -40,6 +40,7 @@ from typing import ( from sqlalchemy.orm import DeclarativeBase from typing_extensions import Self +from reflex import event from reflex.config import get_config from reflex.istate.data import RouterData from reflex.vars.base import ( @@ -2094,7 +2095,8 @@ class State(BaseState): class FrontendEventExceptionState(State): """Substate for handling frontend exceptions.""" - def handle_frontend_exception(self, stack: str) -> None: + @event + def handle_frontend_exception(self, stack: str, component_stack: str) -> None: """Handle frontend exceptions. If a frontend exception handler is provided, it will be called. @@ -2102,6 +2104,7 @@ class FrontendEventExceptionState(State): Args: stack: The stack trace of the exception. + component_stack: The stack trace of the component where the exception occurred. """ app_instance = getattr(prerequisites.get_app(), constants.CompileVars.APP)