Skip to content

Instantly share code, notes, and snippets.

@dpraimeyuu
Forked from andywer/_readme.md
Created January 10, 2022 06:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dpraimeyuu/aa5422d180969dbe9ffd6b8c0577c978 to your computer and use it in GitHub Desktop.
Save dpraimeyuu/aa5422d180969dbe9ffd6b8c0577c978 to your computer and use it in GitHub Desktop.
React - Functional error boundaries

React - Functional error boundaries

Thanks to React hooks you have now happily turned all your classes into functional components.

Wait, all your components? Not quite. There is one thing that can still only be implemented using classes: Error boundaries.

There is just no functional equivalent for componentDidCatch and deriveStateFromError yet.

Proposed solution

The proposed solution is greatly inspired by the new React.memo() API.

import Catch from "./functional-error-boundary"

type Props = {
  children: React.ReactNode
}

const MyErrorBoundary = Catch(function MyErrorBoundary(props: Props, error?: Error) {
  if (error) {
    return (
      <div className="error-screen">
        <h2>An error has occured</h2>
        <h4>{error.message}</h4>
      </div>
    )
  } else {
    return <React.Fragment>{props.children}</React.Fragment>
  }
})

API

type ErrorHandler = (error: Error, info: React.ErrorInfo) => void

Catch(component: React.ComponentType)

Catch(component: React.ComponentType, errorHandler: ErrorHandler)

Wraps the functional component component to make it an error boundary. The caught error is passed to the component function as a second parameter.

The optional second argument to Catch() (errorHandler) is a function that acts as componentDidCatch(). Use it to handle side effects like error logging.

Implementation

Find the code below.

Outlook

Would be great to see something like this integrated into the React.js core library:

import React from "react"

type Props = {
  children: React.ReactNode
}

const MyErrorBoundary = React.Catch(function MyErrorBoundary(props: Props, error?: Error) {
  if (error) {
    return (
      <div className="error-screen">
        <h2>An error has occured</h2>
        <h4>{error.message}</h4>
      </div>
    )
  } else {
    return <React.Fragment>{props.children}</React.Fragment>
  }
})
import React from "react"
type ErrorHandler = (error: Error, info: React.ErrorInfo) => void
type ErrorHandlingComponent<Props> = (props: Props, error?: Error) => React.ReactNode
type ErrorState = { error?: Error }
export default function Catch<Props extends {}>(
component: ErrorHandlingComponent<Props>,
errorHandler?: ErrorHandler
): React.ComponentType<Props> {
return class extends React.Component<Props, ErrorState> {
state: ErrorState = {
error: undefined
}
static getDerivedStateFromError(error: Error) {
return { error }
}
componentDidCatch(error: Error, info: React.ErrorInfo) {
if (errorHandler) {
errorHandler(error, info)
}
}
render() {
return component(this.props, this.state.error)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment