Monolune

Examples of ReactJS Programs in OCaml Syntax

BuckleScript, a compiler from ReasonML/OCaml to JavaScript, seems to be oriented towards ReasonML syntax rather than OCaml syntax. The ReasonReact project, which provides bindings for React, is also oriented towards ReasonML syntax. Its documentation contains little to no information on using ReasonReact with OCaml syntax as an alternative to ReasonML syntax. Some of us may prefer OCaml syntax for reasons of familiarity and aesthetics. This article provides examples of ReactJS programs written using OCaml syntax. I will provide minimal explanation of the code as they are almost one-to-one translations of the corresponding ReasonML code.

This article assumes that you have installed the BuckleScript compiler, that you have a BuckleScript project set up, and that you have installed reason-react as a development dependency (npm --save-dev reason-react). In addition, you should have a JavaScript module bundler (for example, webpack or browserify) that can take the *.bs.js files produced by the BuckleScript compiler to produce JavaScript files that can run on web browsers.

Basic Workflow

Let's start with a program that prints a string to the console. It does not yet involve React, but this basic example will show you the usual manual workflow for running the resulting JavaScript file in a web browser.

let () = Js.log "Hello Planet"

Save the above as printstring.ml, compile it to JavaScript using the BuckleScript compiler. Using your JavaScript module bundler (e.g. webpack), bundle the *.bs.js file produced by the BuckleScript compiler into a JavaScript file suitable for use in web browsers. Include that JavaScript file in a HTML file (<script src="app.js"></script>), and open the HTML file in a web browser, You should see Hello Planet in the web browser's console. Alternatively, you can run the resulting JavaScript file using NodeJS (node app.js), and you should be able to see Hello Planet printed.

Stateless Component

Here's an example of a stateless React component. It does not allow mutable state (e.g. the equivalent of this.setState({...}) in JavaScript).

let component = ReasonReact.statelessComponent "Greeting"

let make _children = {
  component with
  render =
    (fun _self ->
       ReactDOMRe.createElement "p" [| ReasonReact.string "Hello" |])
}

let () = ReactDOMRe.renderToElementWithId (ReasonReact.element (make [||])) "root"

Save the above as hello.ml, and compile it to a bundled JavaScript file named app.js. The HTML file you can use to include app.js is:

<!DOCTYPE html>
<html>
  <head>
    <title>Example</title>
    <meta charset="utf-8">
  </head>
  <body>
    <div id="root"></div>
    <script src="app.js"></script>
  </body>
</html>

Open the HTML file and you should see Hello displayed in the browser.

Stateful Component

Here's an example of a stateful component in the form of a counter that increments whenever a button is clicked:

type state = {
  count: int;
  show: bool;
}

type action =
  | Click
  | Toggle

let component = ReasonReact.reducerComponent "Example"

let make ~greeting _children = {
  component with
  initialState = (fun () -> {
        count = 0;
        show = true;
      });
  reducer = (fun action state ->
      match action with
      | Click -> ReasonReact.Update {
          state with
          count = (state.count + 1)
        }
      | Toggle -> ReasonReact.Update {
          state with
          show = (not state.show)
        });
  render = (fun self ->
      let message = "You've clicked this " ^ (string_of_int (self.state).count) ^ " times(s)" in
      ReactDOMRe.createElement "div" [|
        ReactDOMRe.createElement "button"
          ~props:(ReactDOMRe.props ~onClick:(fun _event -> self.send Click) ())
          [| ReasonReact.string message |];
        ReactDOMRe.createElement "button"
          ~props:(ReactDOMRe.props ~onClick:(fun _event -> self.send Toggle) ())
          [| ReasonReact.string "Toggle greeting" |];
        (match (self.state).show with
         | true -> ReasonReact.string greeting
         | false -> ReasonReact.null);
      |]);
}

let () = ReactDOMRe.renderToElementWithId (ReasonReact.element (make ~greeting:"Hello" [||])) "root"

Save this file as counter.ml, compile it, and include it in a HTML file. You should see a counter that increments whenever the button beside it is clicked.

Conclusion

I hope the above examples have been useful to you in finding out how to write React programs using OCaml. If in the future you find some ReasonML code that you wish to translate to OCaml, you can use the bsrefmt program that is included with the installation of bs-platform. To translate a ReasonML file, do bsrefmt test.re --print=ml, and the OCaml equivalent will be printed to stdout. In this way, you can discover for yourself the equivalent OCaml code of all the examples given in the ReasonReact documentation. Referring to the two examples above, you may find the code messy because of the lack of JSX. There are third-party JSX ppx packages available that allow you to intersperse JSX into the OCaml code, but use them at your own risk.