diff --git a/CHANGES.md b/CHANGES.md index 3b858709..d1dd3df0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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 diff --git a/flake.lock b/flake.lock index f08386fd..fd24c3ed 100644 --- a/flake.lock +++ b/flake.lock @@ -41,11 +41,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1716844469, - "narHash": "sha256-2Edbn0TOoBC7+AY0rQeoYPFRWK89lVVgrGZxwxYX9rU=", + "lastModified": 1718486791, + "narHash": "sha256-Et9ljkLbBEtyyFCV57CH7WQM08ELMAcwcQYUdNz01Wg=", "owner": "nix-ocaml", "repo": "nix-overlays", - "rev": "cd34c0a555802e83b38196719c094c335a6c4f81", + "rev": "3f444cc0c8612c2bf0bf8822da98efbb977e2ff8", "type": "github" }, "original": { @@ -56,17 +56,17 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1716792620, - "narHash": "sha256-wQmXzee/veETYJv93TkRYsAQkEdt2QYCJeJil5SrJfg=", + "lastModified": 1718083092, + "narHash": "sha256-EQsPXycAbmby4meQUNLYfFaGOiqz2J9AlwFRV4UiHnY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "7d7cf1590c05d799745bf456f2b95b798f48d3bb", + "rev": "aa1ebdaf49a606e21c06e0f6ed7aece9a41831c3", "type": "github" }, "original": { "owner": "NixOS", "repo": "nixpkgs", - "rev": "7d7cf1590c05d799745bf456f2b95b798f48d3bb", + "rev": "aa1ebdaf49a606e21c06e0f6ed7aece9a41831c3", "type": "github" } }, diff --git a/flake.nix b/flake.nix index c42cef48..14c4146e 100644 --- a/flake.nix +++ b/flake.nix @@ -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 ]; diff --git a/src/ReasonReactErrorBoundary.re b/src/ReasonReactErrorBoundary.re index 9633ebd9..a4a165ad 100644 --- a/src/ReasonReactErrorBoundary.re +++ b/src/ReasonReactErrorBoundary.re @@ -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;