Skip to content

Commit

Permalink
refactor: convert error boundary code to Reason (#839)
Browse files Browse the repository at this point in the history
* refactor: convert error boundary code to Reason

* tweakg

* no need for warnings

* add changelog

* wip
  • Loading branch information
anmonteiro committed Jun 15, 2024
1 parent 9da6b48 commit 39a146f
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 30 deletions.
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# Unreleased

* Convert `ReasonReactErrorBoundary` to Reason instead of `%raw` JS. This has
the benefit of skipping a hardcoded `require('react')` call (@anmonteiro in
[#839](https://github.com/reasonml/reason-react/pull/839))


# 0.14.1

* Support JSX transform with fragments (@tatchi in
Expand Down
14 changes: 7 additions & 7 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
};
# Due to a Reason version mismatch, the generated OCaml PPX diff
# looks different
doCheck = true;
doCheck = false;
checkInputs = [ ];
checkPhase = "dune build @runtest -p reason-react,reason-react-ppx";
nativeCheckInputs = [ reason merlin pkgs.jq ];
Expand Down
69 changes: 47 additions & 22 deletions src/ReasonReactErrorBoundary.re
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,57 @@ type params('error) = {
info,
};

[%%mel.raw
{|
var React = require("react");
var ErrorBoundary = (function (Component) {
function ErrorBoundary(props) {
Component.call(this);
this.state = {error: undefined};
};
ErrorBoundary.prototype = Object.create(Component.prototype);
ErrorBoundary.prototype.componentDidCatch = function(error, info) {
this.setState({error: {error: error, info: info}});
};
ErrorBoundary.prototype.render = function() {
return this.state.error != undefined
? this.props.fallback(this.state.error)
: this.props.children
};
return ErrorBoundary;
})(React.Component);
|}
];
[@mel.scope "Object"] external objCreate: 'a => Js.t({..}) = "create";

type reactComponentClass;

[@mel.module "react"] external component: reactComponentClass = "Component";
[@mel.send] external componentCall: (reactComponentClass, _) => unit = "call";

type componentPrototype;
[@mel.get]
external componentPrototype: reactComponentClass => componentPrototype =
"prototype";

let errorBoundary =
[@mel.this]
(
(self, _props) => {
componentCall(component, self);
self##state #= {"error": Js.undefined};
}
);

[@mel.set] external setPrototype: (_, _) => unit = "prototype";
setPrototype(errorBoundary, objCreate(componentPrototype(component)));

[@mel.set] [@mel.scope "prototype"]
external setComponentDidCatch:
(_, [@mel.this] (('self, 'error, 'info) => unit)) => unit =
"componentDidCatch";

setComponentDidCatch(errorBoundary, [@mel.this] (self, error, info) => {
self##setState({
"error": {
error,
info,
},
})
});

[@mel.set] [@mel.scope "prototype"]
external setRender: (_, [@mel.this] ('self => unit)) => unit = "render";
setRender(errorBoundary, [@mel.this] self => {
switch (Js.Undefined.testAny(self##state##error)) {
| false => self##props##fallback(self##state##error)
| true => self##props##children
}
});

[@react.component]
external make:
(~children: React.element, ~fallback: params('error) => React.element) =>
React.element =
"ErrorBoundary";
"errorBoundary";

let make = make;

0 comments on commit 39a146f

Please sign in to comment.