82 lines
2.1 KiB
React
82 lines
2.1 KiB
React
import { Component } from "react";
|
|
import { formatMessage } from "../i18n/index.js";
|
|
|
|
/**
|
|
* @typedef {{
|
|
* children: React.ReactNode,
|
|
* boundaryName: string,
|
|
* routeId: string,
|
|
* buildId: string,
|
|
* resetKey?: string,
|
|
* onRenderFailure?: (report: import("../../application/ports/in/application-api.js").RenderFailureReport) => void,
|
|
* 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.onRenderFailure?.({
|
|
routeId: this.props.routeId,
|
|
buildId: this.props.buildId,
|
|
boundaryName:
|
|
/** @type {"route" | "feature"} */ (this.props.boundaryName),
|
|
});
|
|
} catch {
|
|
// Diagnostics must never recurse into another render failure.
|
|
}
|
|
}
|
|
|
|
/** @param {RenderBoundaryProps} previous */
|
|
componentDidUpdate(previous) {
|
|
if (
|
|
this.state.hasError &&
|
|
previous.resetKey !== this.props.resetKey
|
|
) {
|
|
this.setState({ hasError: false });
|
|
}
|
|
}
|
|
|
|
reset = () => {
|
|
this.setState({ hasError: false });
|
|
};
|
|
|
|
render() {
|
|
if (this.state.hasError) {
|
|
return (
|
|
this.props.fallback ?? (
|
|
<section role="alert">
|
|
<p>{formatMessage("ko-KR", "error.render_failure")}</p>
|
|
<button type="button" onClick={this.reset}>
|
|
{formatMessage("ko-KR", "action.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" />;
|
|
}
|