# Trying to learn a bit of React/FRP (not ReactJS)

**URL:** <https://discuss.ocaml.org/t/trying-to-learn-a-bit-of-react-frp-not-reactjs/11453>\
**Category:** Learning\
**Created:** [February 19, 2023, 2:19pm UTC](https://discuss.ocaml.org/t/trying-to-learn-a-bit-of-react-frp-not-reactjs/11453 "2023-02-19T14:19:50Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![benjamin-thomas](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/benjamin-thomas/32/3966_2.png) [@benjamin-thomas](https://discuss.ocaml.org/u/benjamin-thomas)\
**Post date:** [February 19, 2023, 2:19pm UTC](https://discuss.ocaml.org/t/trying-to-learn-a-bit-of-react-frp-not-reactjs/11453/1 "2023-02-19T14:19:50Z")

</div>

I’m interested to learn more about the Ocsigen stack.

For this reason, I’d also like to learn and understand [React / Erratique](https://erratique.ch/logiciel/react), in a JSOO context.

Here’s my dune file:

```plaintext
(executable
 (name simple_counter)
 (libraries
  js_of_ocaml-ppx
  lwt
  lwt_ppx
  js_of_ocaml-lwt
  tyxml
  js_of_ocaml-tyxml
  react
  reactiveData)
 (preprocess
  (pps js_of_ocaml-ppx lwt_ppx))
 (modes js))

```

I’m trying to implement a simple incrementing counter. The following code compiles, however I’m not triggering a new signal on button click.

```auto
(*
 * MODEL
 *)
type model = int
type rs = model React.signal
type rf = ?step:React.step -> model -> unit
type rp = rs * rf

let init : model = 0

(*
 * UPDATE
 *)

type action = Click | NOOP [@@warning "-37"]

let update action ((r, f) : rp) =
  let current_model = React.S.value r in
  let new_model =
    match action with
    | NOOP -> current_model
    | Click -> current_model + 1
  in
  f new_model
;;

(*
 * VIEW
 *)

let view_btn ((r, _f) : rp) =
  let model = React.S.value r in
  Js_of_ocaml_tyxml.Tyxml_js.Html5.(button [txt (string_of_int model)])
;;

let view_btn_dom (rp : rp) =
  Js_of_ocaml_tyxml.Tyxml_js.To_dom.of_button (view_btn rp)
;;

let view ((r, f) : rp) =
  Js_of_ocaml_lwt.Lwt_js_events.(
    async (fun () ->
        clicks
          (view_btn_dom (r, f))
          (fun _evt _ -> Lwt.return @@ update Click (r, f))))

  ; Js_of_ocaml_tyxml.Tyxml_js.To_dom.of_button @@ view_btn (r, f)
;;

let main (_ : Js_of_ocaml.Dom_html.event Js_of_ocaml.Js.t) =
  let document = Js_of_ocaml.Dom_html.document in
  Js_of_ocaml.Dom.appendChild document##.body (view @@ React.S.create init)
  ; Lwt.return ()
;;

let _ =
  let%lwt evt = Js_of_ocaml_lwt.Lwt_js_events.onload () in
  Lwt.return @@ main evt
;;

```

Any pointers would be much appreciated 🙂

---

<div class="post-metadata">

**Author:** ![benjamin-thomas](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/benjamin-thomas/32/3966_2.png) [@benjamin-thomas](https://discuss.ocaml.org/u/benjamin-thomas)\
**Post date:** [February 21, 2023, 7:23pm UTC](https://discuss.ocaml.org/t/trying-to-learn-a-bit-of-react-frp-not-reactjs/11453/2 "2023-02-21T19:23:52Z")

</div>

Baby step n°2: update a clock within a web UI, a simpler example.

Inspired by the [documented example which outputs the current time to a terminal](https://erratique.ch/software/react/doc/React/index.html#clock).

I _think_ I’m getting the hang of this. Posting here for reference, I have yet to get back to my original difficulty.

```auto
(executable
 (name main)
 (libraries js_of_ocaml react unix)
 (preprocess
  (pps js_of_ocaml-ppx))
 (modes js))

```

Type annotations are for clarity:

```auto
open Js_of_ocaml

let (seconds_stream : float React.event), (start_clock : unit -> unit) =
  let evt, send = React.E.create () in
  let do_send () =
    send (Unix.gettimeofday ())
    ; print_endline "time changed"
  in
  let do_schedule () =
    Dom_html.window##setInterval (Js.wrap_callback do_send) 1000.0
    |> ignore (*interval ID*)
  in

  (evt, do_schedule)
;;

let create_button () =
  let button = Dom_html.(createButton document) in
  let div = Dom_html.(createDiv document) in
  Dom.appendChild div button
  ; Dom.appendChild Dom_html.document##.body div
  ; button
;;

let button = create_button ()

let update_btn (t : float) : unit =
  let h, m, s =
    let tm = Unix.localtime t in
    (tm.Unix.tm_hour, tm.Unix.tm_min, tm.Unix.tm_sec)
  in
  let tm_s = Printf.sprintf "%02d:%02d:%02d" h m s in

  button##.innerText := Js.string tm_s
;;

let start_update_btn () =
  let (_ : unit React.event) = React.E.map update_btn seconds_stream in
  ()
;;

let () =
  start_update_btn ()
  ; start_clock ()
;;

```

I’m interested in finding simple examples like this. I haven’t found much so far.

---

<div class="post-metadata">

**Author:** ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)\
**Post date:** [February 21, 2023, 7:50pm UTC](https://discuss.ocaml.org/t/trying-to-learn-a-bit-of-react-frp-not-reactjs/11453/3 "2023-02-21T19:50:16Z")

</div>

> [@benjamin-thomas](#):
>
> I’m interested in finding simple examples like this. I haven’t found much so far.

There’s a todomvc with note [here](https://github.com/dbuenzli/brr/blob/9540f82cf1139968dcb5067a726541ced5f31286/test/todomvc.ml). This [gesture recognizer](https://erratique.ch/log/2017-01-25/) uses react you can find the source at the end of the blog post.

---

<div class="post-metadata">

**Author:** ![benjamin-thomas](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/benjamin-thomas/32/3966_2.png) [@benjamin-thomas](https://discuss.ocaml.org/u/benjamin-thomas)\
**Post date:** [February 22, 2023, 7:44pm UTC](https://discuss.ocaml.org/t/trying-to-learn-a-bit-of-react-frp-not-reactjs/11453/4 "2023-02-22T19:44:11Z")

</div>

Thanks for the heads up! I’ll be sure to dig into those examples, once I fully grokked the basics.

So I managed to get my baby step #1 going:

```auto
open Js_of_ocaml

let (counter, change_counter) = React.S.create 0

let create_ui () =
  let body = Dom_html.document##.body in
  let div = Dom_html.(createDiv document) in
  let h1 = Dom_html.(createH1 document) in
  let input = Dom_html.(createInput document) in
  let btn = Dom_html.(createButton document) in

  ()
  ; h1##.innerText := Js.string "Counter example"
  ; btn##.innerText := Js.string "count"
  ; input##.disabled := Js.bool true
  ; Dom.appendChild div h1
  ; Dom.appendChild div input
  ; Dom.appendChild div btn
  ; Dom.appendChild body div
  ; (input, btn)
;;

let (input, btn) = create_ui ()

let start_listen_click () =
  btn##.onclick :=
    Dom_html.handler (fun _ ->
        let curr = React.S.value counter in
        change_counter (curr + 1)
        ; Js.bool false)
;;

let update_input n = input##.value := Js.string (string_of_int n)
let start_update_input () = React.S.map update_input counter |> ignore

let () =
  start_update_input ()
  ; start_listen_click ()
;;

```

Based on those definitions:

> An event is a value with discrete occurrences over time.

> A signal is a value that varies continuously over time. In contrast to events which occur at specific point in time, a signal has a value at every point in time.

I wanted to use an event to describe the mouse clicks, but to make things work I had to resort to using a signal instead.

To me, it sounds like the mouse position should be a signal, but a mouse click should be an event.

I’ll keep digging…

---

<div class="post-metadata">

**Author:** ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)\
**Post date:** [February 22, 2023, 9:41pm UTC](https://discuss.ocaml.org/t/trying-to-learn-a-bit-of-react-frp-not-reactjs/11453/5 "2023-02-22T21:41:19Z")

</div>

If you want to inspire yourself there’s quite a bit of code that interfaces with DOM events in [Useri / Erratique](https://erratique.ch/software/useri) or [Brr\_note\_kit (brr.Brr\_note\_kit)](https://erratique.ch/software/brr/doc/Brr_note_kit/index.html)

---

<div class="post-metadata">

**Author:** ![benjamin-thomas](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/benjamin-thomas/32/3966_2.png) [@benjamin-thomas](https://discuss.ocaml.org/u/benjamin-thomas)\
**Post date:** [February 23, 2023, 5:36pm UTC](https://discuss.ocaml.org/t/trying-to-learn-a-bit-of-react-frp-not-reactjs/11453/6 "2023-02-23T17:36:00Z")

</div>

Thanks 🙂

Brr is on my todo list.

It looks like you initially intended to release `useri` but then decided not to?

```auto
$ opam install js_of_ocaml useri
[ERROR] No package named useri found.

```

My main motivation for looking into React results from having seen the possibility to share a signal between the client and server, with type safety 🤯

So now, I want to dig in and understand the details. The fact that I can see that React may be applied to other domains than web makes it even more so interesting.

Here’s a baby step #3, in a more declarative style this time, using ReactiveData which uses React under the hood.

`Js_of_ocaml_tyxml` provides the ability to define “reactive nodes” via its module `Tyxml_js.R.Html`.

Adapted from one of the examples available at: [OCaml toplevel](https://ocsigen.org/js_of_ocaml/latest/manual/files/toplevel/index.html)

```auto
open Js_of_ocaml
open Js_of_ocaml_lwt
open Js_of_ocaml_tyxml
module RList = ReactiveData.RList

(*
 * REACTIVE DATA
 *)

let (r_lst, r_handle) = RList.create []
let cons s = RList.cons s r_handle

(* snoc is the inverse of cons *)
let snoc s = RList.snoc s r_handle

let time_signal =
  let (s, set) = React.S.create (Sys.time ()) in
  let rec loop () : unit Lwt.t =
    let t = Sys.time () in
    ()
    ; set t
    ; if t < 10.0 then snoc "Will add every 1s (until uptime=10s)"
    ; Lwt.bind (Lwt_js.sleep 1.) loop
  in
  Lwt.async loop
  ; s
;;

(*
 * REACTIVE VIEW
 *)

let r_li = RList.map (fun x -> Tyxml_js.Html.(li [txt x])) r_lst
let r_ul = Tyxml_js.R.Html.ul r_li

let r_view =
  Tyxml_js.(
    Html.(
      div
        [ h1
            [ txt "Uptime is "
            ; R.Html.txt
                (React.S.map (fun s -> Printf.sprintf "%.0fs" s) time_signal)
            ]
        ; r_ul
        ]))
;;

(*
 * BOOTSTRAP
 *)

let append_to_body elt =
  Dom.appendChild Dom_html.document##.body (Tyxml_js.To_dom.of_element elt)
;;

(*

This program outputs something like this:

Uptime is 99s

- Item 3
- Item 2
- Item 1
- Will add every 1s (until uptime=10s)
- Item 4
- Will add every 1s (until uptime=10s)
- Will add every 1s (until uptime=10s)
- Will add every 1s (until uptime=10s)
- Will add every 1s (until uptime=10s)
- Will add every 1s (until uptime=10s)
- Will add every 1s (until uptime=10s)
- Will add every 1s (until uptime=10s)
- Will add every 1s (until uptime=10s)
- Will add every 1s (until uptime=10s)

*)

let () =
  ()
  ; cons "Item 1"
  ; cons "Item 2"
  ; cons "Item 3"
  ; snoc "Item 4"
  ; append_to_body r_view
;;

```

---

<div class="post-metadata">

**Author:** ![benjamin-thomas](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/benjamin-thomas/32/3966_2.png) [@benjamin-thomas](https://discuss.ocaml.org/u/benjamin-thomas)\
**Post date:** [February 23, 2023, 8:05pm UTC](https://discuss.ocaml.org/t/trying-to-learn-a-bit-of-react-frp-not-reactjs/11453/7 "2023-02-23T20:05:20Z")

</div>

Okay it’s finally clicking in for me.

As a last baby step, here’s a declarative style version of what I was trying to do at the opening of my post. My original attempt looks so cute in comparaison 🤣

Pretty fun!

```auto
open Js_of_ocaml
open Js_of_ocaml_tyxml

(*
 * SETUP
 *)

let (counter, set_counter) = React.S.create 0

(*
 * REACTIVE VIEW
 *)

let r_input =
  (* The input's background turns increasingly red, as the button is clicked. *)
  let bg_towards_red c =
    let bg n =
      let n =
        if n < 0 then
          0
        else
          n
      in
      Printf.sprintf "background-color: rgb(255, %d, %d)" n n
    in
    bg (255 - (c * 10))
  in
  Tyxml_js.R.Html.(
    input
      ~a:
        [ a_style @@ React.S.map bg_towards_red counter
        ; a_disabled ()
        ; a_value @@ React.S.map string_of_int counter
        ])
    ()
;;

let button' =
  Tyxml_js.Html.(
    button
      ~a:
        [ a_onclick (fun _ ->
              let curr = React.S.value counter in
              set_counter (curr + 1)
              ; false)
        ]
      [txt "counter"])
;;

let r_view =
  Tyxml_js.(
    Html.(div [h1 [ txt "Counter example"]; div [r_input; button'] ]))
;;

(*
 * BOOTSTRAP
 *)

let append_to_body elt =
  Dom.appendChild Dom_html.document##.body (Tyxml_js.To_dom.of_element elt)
;;

let () = append_to_body r_view

```
