Skip to content

Commit f6b37b8

Browse files
matthidalfonsogarciacaro
authored andcommitted
add react-error-boundaries
1 parent 78e259a commit f6b37b8

File tree

2 files changed

+85
-1
lines changed

2 files changed

+85
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Fable bindings and helpers for React projects
66

77
* [Server-Side Rendering tutorial](docs/server-side-rendering.md): A **Pure F#** solution for SSR, **No NodeJS Required!**
88
* [Using third party React components](docs/using-third-party-react-components.md): How to create binding so that third party Javascript React components can be used like stock React components in Fable code.
9-
9+
* [React error boundaries](docs/react-error-boundaries.md): Example on how to use react error boundaries in fable.
1010

1111

1212
## Why does this repository include bindings for React JS libraries?

docs/react-error-boundaries.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# How to use react error boundaries in fable
2+
3+
When react renders the view and encounters an error it will by default just crash the complete application.
4+
If your html page only contains a single react element your page will be essentially a white page from this point and the user needs to reload the website.
5+
6+
The problem is that a simple `try-catch` in the view will not be enough to handle errors in the render-pipeline.
7+
In order to catch these errors you need to overwrite `componentDidCatch`, this process is documented [here](https://reactjs.org/docs/error-boundaries.html)
8+
9+
While render-errors are rare when using fable it can happen, especially when embedding 3rd-party-components.
10+
Because of this we provide a general purpose ReactErrorBoundary component which can be added and used from your application (including elmish-architecture)
11+
12+
13+
## ReactErrorBoundaries.fs
14+
15+
Just copy the following code to your project:
16+
17+
```fsharp
18+
module ReactErrorBoundary
19+
20+
open Fable.Core
21+
open Fable.Import
22+
open Fable.Helpers.React
23+
24+
type [<AllowNullLiteral>] InfoComponentObject =
25+
abstract componentStack: string with get
26+
27+
[<Pojo>]
28+
type ErrorBoundaryProps =
29+
{ Inner : React.ReactElement
30+
ErrorComponent : React.ReactElement
31+
OnError : exn * InfoComponentObject -> unit }
32+
33+
[<Pojo>]
34+
type ErrorBoundaryState =
35+
{ HasErrors : bool }
36+
37+
// See https://github.com/MangelMaxime/Fulma/blob/master/docs/src/Widgets/Showcase.fs
38+
// See https://reactjs.org/docs/error-boundaries.html
39+
type ErrorBoundary(props) =
40+
inherit React.Component<ErrorBoundaryProps, ErrorBoundaryState>(props)
41+
do base.setInitState({ HasErrors = false })
42+
43+
override x.componentDidCatch(error, info) =
44+
let info = info :?> InfoComponentObject
45+
x.props.OnError(error, info)
46+
x.setState({ HasErrors = true })
47+
48+
override x.render() =
49+
if (x.state.HasErrors) then
50+
x.props.ErrorComponent
51+
else
52+
x.props.Inner
53+
54+
let renderCatchSimple errorElement element =
55+
ofType<ErrorBoundary,_,_> { Inner = element; ErrorComponent = errorElement; OnError = fun _ -> () } [ ]
56+
57+
let renderCatchFn onError errorElement element =
58+
ofType<ErrorBoundary,_,_> { Inner = element; ErrorComponent = errorElement; OnError = onError } [ ]
59+
```
60+
61+
## Usage
62+
63+
Usage from an elmish application could look similar to this:
64+
65+
Consider you have a `SubComponent` which might run into rendering issues and an `ErrorComponent` you want to show if a rendering issue occurs:
66+
67+
```fsharp
68+
let view model dispatch =
69+
SubComponent.view model dispatch
70+
|> ReactErrorBoundary.renderCatchFn
71+
(fun (error, info) ->
72+
//dispatch (MyMessage ...)
73+
logger.Error("SubComponent failed to render" + info.componentStack, error))
74+
(ErrorComponent.view model dispatch)
75+
```
76+
77+
If you don't need the `OnError` event:
78+
79+
```fsharp
80+
let view model dispatch =
81+
SubComponent.view model dispatch
82+
|> ReactErrorBoundary.renderCatchSimple
83+
(ErrorComponent.view model dispatch)
84+
```

0 commit comments

Comments
 (0)