Fix the error boundary (#3637)
* - added possibility to pass front- and backend exception handlers to rx.App - wrapped the app with ErrorBoundary component to catch rendering errors * added missing exception handler to reflex.app._process * regenerated pyi * fix unit tests for exception handler validation * fix typing error in ErrorBoudary * - fix error_bounday pyi - minor refactoring of error boundary - removed error boundary from 404 page * added missing inspect module import after merging main * fix typing error * Clean up ErrorBoundary component * Remove custom _render function * Remove special imports in Component base class * Simplify hooks for better composability * test_exception_handlers: also test in prod mode * fixed color prop * fixed error boundary bug and removed hardcoding of the frontend event exception state * formatted the code after conflict resolution * fixed type of the error_boundary * ruff format --------- Co-authored-by: Maxim Vlah <m.vlah@senbax.de> Co-authored-by: Masen Furer <m_github@0x26.net>
This commit is contained in:
parent
5071245127
commit
fa71edd224
@ -26,6 +26,8 @@ export const clientStorage = {}
|
||||
{% if state_name %}
|
||||
export const state_name = "{{state_name}}"
|
||||
|
||||
export const exception_state_name = "{{const.frontend_exception_state}}"
|
||||
|
||||
// Theses events are triggered on initial load and each page navigation.
|
||||
export const onLoadInternalEvent = () => {
|
||||
const internal_events = [];
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
initialState,
|
||||
onLoadInternalEvent,
|
||||
state_name,
|
||||
exception_state_name,
|
||||
} from "utils/context.js";
|
||||
import debounce from "/utils/helpers/debounce";
|
||||
import throttle from "/utils/helpers/throttle";
|
||||
@ -698,7 +699,7 @@ export const useEventLoop = (
|
||||
}
|
||||
|
||||
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
||||
addEvents([Event("state.frontend_event_exception_state.handle_frontend_exception", {
|
||||
addEvents([Event(`${exception_state_name}.handle_frontend_exception`, {
|
||||
stack: error.stack,
|
||||
})])
|
||||
return false;
|
||||
@ -707,7 +708,7 @@ export const useEventLoop = (
|
||||
//NOTE: Only works in Chrome v49+
|
||||
//https://github.com/mknichel/javascript-errors?tab=readme-ov-file#promise-rejection-events
|
||||
window.onunhandledrejection = function (event) {
|
||||
addEvents([Event("state.frontend_event_exception_state.handle_frontend_exception", {
|
||||
addEvents([Event(`${exception_state_name}.handle_frontend_exception`, {
|
||||
stack: event.reason.stack,
|
||||
})])
|
||||
return false;
|
||||
|
@ -132,14 +132,17 @@ def default_overlay_component() -> Component:
|
||||
)
|
||||
|
||||
|
||||
def default_error_boundary() -> Component:
|
||||
def default_error_boundary(*children: Component) -> Component:
|
||||
"""Default error_boundary attribute for App.
|
||||
|
||||
Args:
|
||||
*children: The children to render in the error boundary.
|
||||
|
||||
Returns:
|
||||
The default error_boundary, which is an ErrorBoundary.
|
||||
|
||||
"""
|
||||
return ErrorBoundary.create()
|
||||
return ErrorBoundary.create(*children)
|
||||
|
||||
|
||||
class OverlayFragment(Fragment):
|
||||
@ -184,9 +187,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
||||
)
|
||||
|
||||
# Error boundary component to wrap the app with.
|
||||
error_boundary: Optional[Union[Component, ComponentCallable]] = (
|
||||
default_error_boundary
|
||||
)
|
||||
error_boundary: Optional[ComponentCallable] = default_error_boundary
|
||||
|
||||
# Components to add to the head of every page.
|
||||
head_components: List[Component] = []
|
||||
@ -751,7 +752,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
||||
if self.error_boundary is None:
|
||||
return component
|
||||
|
||||
component = ErrorBoundary.create(*component.children)
|
||||
component = self.error_boundary(*component.children)
|
||||
|
||||
return component
|
||||
|
||||
|
@ -44,6 +44,7 @@ class ReflexJinjaEnvironment(Environment):
|
||||
"hydrate": constants.CompileVars.HYDRATE,
|
||||
"on_load_internal": constants.CompileVars.ON_LOAD_INTERNAL,
|
||||
"update_vars_internal": constants.CompileVars.UPDATE_VARS_INTERNAL,
|
||||
"frontend_exception_state": constants.CompileVars.FRONTEND_EXCEPTION_STATE,
|
||||
}
|
||||
|
||||
|
||||
|
@ -65,6 +65,8 @@ class CompileVars(SimpleNamespace):
|
||||
ON_LOAD_INTERNAL = "on_load_internal_state.on_load_internal"
|
||||
# The name of the internal event to update generic state vars.
|
||||
UPDATE_VARS_INTERNAL = "update_vars_internal_state.update_vars_internal"
|
||||
# The name of the frontend event exception state
|
||||
FRONTEND_EXCEPTION_STATE = "state.frontend_event_exception_state"
|
||||
|
||||
|
||||
class PageNames(SimpleNamespace):
|
||||
@ -124,14 +126,14 @@ class Hooks(SimpleNamespace):
|
||||
}
|
||||
})"""
|
||||
|
||||
FRONTEND_ERRORS = """
|
||||
const logFrontendError = (error, info) => {
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
addEvents([Event("frontend_event_exception_state.handle_frontend_exception", {
|
||||
FRONTEND_ERRORS = f"""
|
||||
const logFrontendError = (error, info) => {{
|
||||
if (process.env.NODE_ENV === "production") {{
|
||||
addEvents([Event("{CompileVars.FRONTEND_EXCEPTION_STATE}.handle_frontend_exception", {{
|
||||
stack: error.stack,
|
||||
})])
|
||||
}
|
||||
}
|
||||
}})])
|
||||
}}
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user