Skip to content

Instantly share code, notes, and snippets.

@matthid
Created September 20, 2018 17:41
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matthid/99b212fcec2b5987baae2d3b667dfe99 to your computer and use it in GitHub Desktop.
Save matthid/99b212fcec2b5987baae2d3b667dfe99 to your computer and use it in GitHub Desktop.
React error boundary for rendering issues with fable, designed to be used with elmish architecture
module ReactErrorBoundary
open Fable.Core
open Fable.Import
open Fable.Helpers.React
type [<AllowNullLiteral>] InfoComponentObject =
abstract componentStack: string with get
[<Pojo>]
type ErrorBoundaryProps =
{ Inner : React.ReactElement
ErrorComponent : React.ReactElement
OnError : exn * InfoComponentObject -> unit }
[<Pojo>]
type ErrorBoundaryState =
{ HasErrors : bool }
// See https://github.com/MangelMaxime/Fulma/blob/master/docs/src/Widgets/Showcase.fs
// See https://reactjs.org/docs/error-boundaries.html
type ErrorBoundary(props) =
inherit React.Component<ErrorBoundaryProps, ErrorBoundaryState>(props)
do base.setInitState({ HasErrors = false })
override x.componentDidCatch(error, info) =
let info = info :?> InfoComponentObject
x.props.OnError(error, info)
x.setState({ HasErrors = true })
override x.render() =
if (x.state.HasErrors) then
x.props.ErrorComponent
else
x.props.Inner
let renderCatchSimple errorElement element =
ofType<ErrorBoundary,_,_> { Inner = element; ErrorComponent = errorElement; OnError = fun _ -> () } [ ]
let renderCatchFn onError errorElement element =
ofType<ErrorBoundary,_,_> { Inner = element; ErrorComponent = errorElement; OnError = onError } [ ]
@matthid
Copy link
Author

matthid commented Sep 20, 2018

Usage:

let view model dispatch =
    SubComponent.view model dispatch
    |> ReactErrorBoundary.renderCatchFn
            (fun (error, info) -> logger.Error("SubComponent failed to render" + info.componentStack, error))
            (ErrorComponent.view model dispatch)
let view model dispatch =
    SubComponent.view model dispatch
    |> ReactErrorBoundary.renderCatchSimple
            (ErrorComponent.view model dispatch)

@kerams
Copy link

kerams commented Jan 10, 2020

Thanks, really nice. I changed errorElement to a function so that it's not constructed unnecessarily on every view cycle even when there are no errors.

@kerams
Copy link

kerams commented Jan 10, 2020

How do I get rid of HasErrors once it's set though? I'd like to revert it once the view in question stops throwing.

@matthid
Copy link
Author

matthid commented Jan 10, 2020

@kerams Usually we resolve the error with some model update (user clicking retry for example) and the view is re-rendered

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment