Search code examples
reasonreason-react

How to handle global DOM events in ReasonML/ReasonReact?


What's the most idiomatic way of listening/handling global DOM events in ReasonML.

I'm building a ReasonReact version of the 2048 game where I need to listen for keyboard events.

In a standard JS/React app I'd have a component with a componentDidMount lifecycle method where I would listen to the event with document.addEventListener("keypress", [my_event_handler]) and unlisten to the same on componentWillUnmount with document.removeEventListener("keypress", [my_event_handler]).

What's the most idiomatic way of accessing document.(addEventListener/removeEventListener) in Reason/ReasonReact?


Solution

  • You can do pretty much the same thing in ReasonReact–it supports the didMount and willUnmount lifecycle methods, which correspond to their ReactJS namesakes: https://reasonml.github.io/reason-react/docs/en/lifecycles.html

    To add and remove event listeners, you can use @glennsl's excellent bs-webapi package: https://redex.github.io/package/bs-webapi

    Here are some examples of adding and removing event listeners: https://github.com/reasonml-community/bs-webapi-incubator/blob/24cee2500b9c98355a14896fa9fc4ceb8a3e2258/tests/dom/events/eventTarget_test.re

    Putting it all together, you could have a component like this:

    /* src/components/MyComponent.re */
    let document = Webapi.Dom.Document.asEventTarget(Webapi.Dom.document);
    let handleKey = _ => Js.log("Key pressed");
    let component = ReasonReact.statelessComponent("MyComponent");
    let make = _children => {
      ...component,
      didMount: _self =>
        Webapi.Dom.EventTarget.addEventListener("keypress", handleKey, document),
      willUnmount: _self =>
        Webapi.Dom.EventTarget.removeEventListener("keypress", handleKey, document),
      render: _self => <p> (ReasonReact.string("Hello")) </p>,
    };