UNPKG

@suspensive/react

Version:

Suspensive interfaces for react

156 lines (153 loc) 6.88 kB
'use client'; import { _objectSpread2 } from "./_virtual/_@oxc-project_runtime@0.130.0/helpers/objectSpread2.mjs"; import { Message_useErrorBoundaryFallbackProps_this_hook_should_be_called_in_ErrorBoundary_props_fallback, Message_useErrorBoundary_this_hook_should_be_called_in_ErrorBoundary_props_children, SuspensiveError } from "./models/SuspensiveError.mjs"; import { ErrorBoundaryGroupContext } from "./ErrorBoundaryGroup.mjs"; import { hasResetKeysChanged } from "./utils/hasResetKeysChanged.mjs"; import { Component, createContext, forwardRef, useContext, useImperativeHandle, useMemo, useRef, useState } from "react"; import { Fragment, jsx } from "react/jsx-runtime"; //#region src/ErrorBoundary.tsx const matchError = (errorMatcher, error) => { if (typeof errorMatcher === "boolean") return errorMatcher; if (typeof errorMatcher === "function") { try { if (errorMatcher === Error || errorMatcher.prototype instanceof Error) return error instanceof errorMatcher; } catch (_unused) {} try { if (error instanceof errorMatcher) return true; } catch (_unused2) {} try { return errorMatcher(error); } catch (_unused3) { return false; } } return false; }; const shouldCatchError = (shouldCatch, error) => Array.isArray(shouldCatch) ? shouldCatch.some((errorMatcher) => matchError(errorMatcher, error)) : matchError(shouldCatch, error); const initialErrorBoundaryState = { isError: false, error: null }; var BaseErrorBoundary = class extends Component { constructor(..._args) { super(..._args); this.state = initialErrorBoundaryState; this.reset = () => { var _this$props$onReset, _this$props; (_this$props$onReset = (_this$props = this.props).onReset) === null || _this$props$onReset === void 0 || _this$props$onReset.call(_this$props); this.setState(initialErrorBoundaryState); }; } static getDerivedStateFromError(error) { return { isError: true, error }; } componentDidUpdate(prevProps, prevState) { const { isError } = this.state; const { resetKeys } = this.props; if (isError && prevState.isError && hasResetKeysChanged(prevProps.resetKeys, resetKeys)) this.reset(); } componentDidCatch(error, info) { var _this$props$onError, _this$props2; (_this$props$onError = (_this$props2 = this.props).onError) === null || _this$props$onError === void 0 || _this$props$onError.call(_this$props2, error, info); } render() { const { children, fallback, shouldCatch = true } = this.props; const { isError, error } = this.state; let childrenOrFallback = children; if (isError) { if (error instanceof SuspensiveError) throw error; if (error instanceof ErrorInFallback) throw error.originalError; if (!shouldCatchError(shouldCatch, error)) throw error; if (typeof fallback === "undefined") { if (process.env.NODE_ENV === "development") console.error("ErrorBoundary of @suspensive/react requires a defined fallback"); throw error; } const Fallback = fallback; childrenOrFallback = /* @__PURE__ */ jsx(FallbackBoundary, { children: typeof Fallback === "function" ? /* @__PURE__ */ jsx(Fallback, { error, reset: this.reset }) : Fallback }); } return /* @__PURE__ */ jsx(ErrorBoundaryContext.Provider, { value: _objectSpread2(_objectSpread2({}, this.state), {}, { reset: this.reset }), children: childrenOrFallback }); } }; var ErrorInFallback = class extends Error { constructor(originalError) { super(); this.originalError = originalError; } }; var FallbackBoundary = class extends Component { componentDidCatch(originalError) { throw originalError instanceof SuspensiveError ? originalError : new ErrorInFallback(originalError); } render() { return this.props.children; } }; /** * This component provides a simple and reusable wrapper that you can use to wrap around your components. Any rendering errors in your components hierarchy can then be gracefully handled. * @see {@link https://suspensive.org/docs/react/ErrorBoundary Suspensive Docs} */ const ErrorBoundary = Object.assign(forwardRef(function ErrorBoundary(props, ref) { var _useContext; const { fallback, children, onError, onReset, resetKeys, shouldCatch } = props; const group = (_useContext = useContext(ErrorBoundaryGroupContext)) !== null && _useContext !== void 0 ? _useContext : { resetKey: 0 }; const baseErrorBoundaryRef = useRef(null); useImperativeHandle(ref, () => ({ reset: () => { var _baseErrorBoundaryRef; return (_baseErrorBoundaryRef = baseErrorBoundaryRef.current) === null || _baseErrorBoundaryRef === void 0 ? void 0 : _baseErrorBoundaryRef.reset(); } })); return /* @__PURE__ */ jsx(BaseErrorBoundary, { shouldCatch, fallback, onError, onReset, resetKeys: [group.resetKey, ...resetKeys || []], ref: baseErrorBoundaryRef, children }); }), { displayName: "ErrorBoundary", with: (errorBoundaryProps, Component) => Object.assign((props) => /* @__PURE__ */ jsx(ErrorBoundary, _objectSpread2(_objectSpread2({}, errorBoundaryProps), {}, { children: /* @__PURE__ */ jsx(Component, _objectSpread2({}, props)) })), { displayName: `${ErrorBoundary.displayName}.with(${Component.displayName || Component.name || "Component"})` }), Consumer: ({ children }) => /* @__PURE__ */ jsx(Fragment, { children: children(useErrorBoundary()) }) }); const ErrorBoundaryContext = Object.assign(createContext(null), { displayName: "ErrorBoundaryContext" }); /** * This hook provides a simple and reusable wrapper that you can use to wrap around your components. Any rendering errors in your components hierarchy can then be gracefully handled. * @see {@link https://suspensive.org/docs/react/ErrorBoundary#useerrorboundary Suspensive Docs} */ const useErrorBoundary = () => { const [state, setState] = useState({ isError: false, error: null }); if (state.isError) throw state.error; const errorBoundary = useContext(ErrorBoundaryContext); SuspensiveError.assert(errorBoundary != null && !errorBoundary.isError, Message_useErrorBoundary_this_hook_should_be_called_in_ErrorBoundary_props_children); return useMemo(() => ({ setError: (error) => setState({ isError: true, error }) }), []); }; /** * This hook allows you to access the reset method and error objects without prop drilling. * @see {@link https://suspensive.org/docs/react/ErrorBoundary#useerrorboundaryfallbackprops Suspensive Docs} */ const useErrorBoundaryFallbackProps = () => { const errorBoundary = useContext(ErrorBoundaryContext); SuspensiveError.assert(errorBoundary != null && errorBoundary.isError, Message_useErrorBoundaryFallbackProps_this_hook_should_be_called_in_ErrorBoundary_props_fallback); return useMemo(() => ({ error: errorBoundary.error, reset: errorBoundary.reset }), [errorBoundary.error, errorBoundary.reset]); }; //#endregion export { ErrorBoundary, useErrorBoundary, useErrorBoundaryFallbackProps }; //# sourceMappingURL=ErrorBoundary.mjs.map