feat: add layered render recovery boundaries
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import { Component } from "react";
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* children: React.ReactNode,
|
||||
* boundaryName: string,
|
||||
* routeId: string,
|
||||
* buildId: string,
|
||||
* telemetry?: import("../../application/ports/telemetry-port.js").TelemetryPort,
|
||||
* fallback?: React.ReactNode
|
||||
* }} RenderBoundaryProps
|
||||
* @typedef {{ hasError: boolean }} RenderBoundaryState
|
||||
*/
|
||||
|
||||
/** @extends {Component<RenderBoundaryProps, RenderBoundaryState>} */
|
||||
export class RenderErrorBoundary extends Component {
|
||||
/** @param {RenderBoundaryProps} props */
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { hasError: false };
|
||||
}
|
||||
|
||||
static getDerivedStateFromError() {
|
||||
return { hasError: true };
|
||||
}
|
||||
|
||||
componentDidCatch() {
|
||||
try {
|
||||
this.props.telemetry?.emit("ui.render.failed", {
|
||||
route_id: this.props.routeId,
|
||||
build_id: this.props.buildId,
|
||||
component_boundary: this.props.boundaryName,
|
||||
});
|
||||
} catch {
|
||||
// Telemetry must never recurse into another render failure.
|
||||
}
|
||||
}
|
||||
|
||||
reset = () => {
|
||||
this.setState({ hasError: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
this.props.fallback ?? (
|
||||
<section role="alert">
|
||||
<p>error.render_failure</p>
|
||||
<button type="button" onClick={this.reset}>
|
||||
retry
|
||||
</button>
|
||||
</section>
|
||||
)
|
||||
);
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {Omit<RenderBoundaryProps, "boundaryName">} props */
|
||||
export function RouteBoundary(props) {
|
||||
return <RenderErrorBoundary {...props} boundaryName="route" />;
|
||||
}
|
||||
|
||||
/** @param {Omit<RenderBoundaryProps, "boundaryName">} props */
|
||||
export function FeatureBoundary(props) {
|
||||
return <RenderErrorBoundary {...props} boundaryName="feature" />;
|
||||
}
|
||||
Reference in New Issue
Block a user