소스 검색

add type hinting to error boundary (#4182)

* add type hinting to error boundary

* remove logFrontendError

* fix other calls to handle_frontend_exception
Khaleel Al-Adhami 7 달 전
부모
커밋
45959881ac
5개의 변경된 파일49개의 추가작업 그리고 42개의 파일을 삭제
  1. 2 0
      reflex/.templates/web/utils/state.js
  2. 35 24
      reflex/components/base/error_boundary.py
  3. 8 7
      reflex/components/base/error_boundary.pyi
  4. 0 10
      reflex/constants/compiler.py
  5. 4 1
      reflex/state.py

+ 2 - 0
reflex/.templates/web/utils/state.js

@@ -743,6 +743,7 @@ export const useEventLoop = (
       addEvents([
         Event(`${exception_state_name}.handle_frontend_exception`, {
           stack: error.stack,
+          component_stack: "",
         }),
       ]);
       return false;
@@ -754,6 +755,7 @@ export const useEventLoop = (
       addEvents([
         Event(`${exception_state_name}.handle_frontend_exception`, {
           stack: event.reason.stack,
+          component_stack: "",
         }),
       ]);
       return false;

+ 35 - 24
reflex/components/base/error_boundary.py

@@ -2,16 +2,30 @@
 
 from __future__ import annotations
 
-from typing import List
+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], Var[str]]:
+    """The spec for the on_error event handler.
+
+    Args:
+        error: The error message.
+        info: Additional information about the error.
+
+    Returns:
+        The arguments for the event handler.
+    """
+    return (
+        error.stack,
+        info.componentStack,
+    )
 
 
 class ErrorBoundary(Component):
@@ -21,31 +35,13 @@ class ErrorBoundary(Component):
     tag = "ErrorBoundary"
 
     # Fired when the boundary catches an error.
-    on_error: EventHandler[lambda error, info: [error, info]] = Var(  # type: ignore
-        "logFrontendError"
-    ).to(FunctionVar, EventChain)
+    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]
-
     def add_custom_code(self) -> List[str]:
         """Add custom Javascript code into the page that contains this component.
 
@@ -75,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

+ 8 - 7
reflex/components/base/error_boundary.pyi

@@ -3,17 +3,18 @@
 # ------------------- DO NOT EDIT ----------------------
 # This file was generated by `reflex/utils/pyi_generator.py`!
 # ------------------------------------------------------
-from typing import Any, Dict, List, Optional, Union, overload
+from typing import Any, Dict, List, Optional, Tuple, Union, overload
 
 from reflex.components.component import Component
 from reflex.event import EventType
 from reflex.style import Style
-from reflex.utils.imports import ImportVar
 from reflex.vars.base import Var
 
+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
@@ -31,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] = None,
+        on_error: Optional[EventType[str, str]] = None,
         on_focus: Optional[EventType[[]]] = None,
         on_mount: Optional[EventType[[]]] = None,
         on_mouse_down: Optional[EventType[[]]] = None,
@@ -45,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.
@@ -59,7 +60,7 @@ class ErrorBoundary(Component):
             **props: The props of the component.
 
         Returns:
-            The component.
+            The ErrorBoundary component.
         """
         ...
 

+ 0 - 10
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."""

+ 4 - 1
reflex/state.py

@@ -39,6 +39,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)