Clock

Clock.ml

module P = Preact

module Clock = struct
  let make =
   fun [@preact.component "Clock"] () ->
    let[@hook] isRunning, setIsRunning = P.useState true in
    let[@hook] now, setNow = P.useState (Js.Date.make ()) in
    let[@hook] () = AnimationFrame.use (isRunning, fun _ -> setNow (Js.Date.make ())) in
    let f n = P.string ((if n < 10.0 then "0" else "") ^ Js.Float.toString n) in
    P.h2
      [ P.style "text-align" "center" ]
      [ f (Js.Date.getHours now)
      ; P.string ":"
      ; f (Js.Date.getMinutes now)
      ; P.string ":"
      ; f (Js.Date.getSeconds now)
      ; P.div
          []
          [ P.button
              [ P.onClick (fun _ -> setIsRunning (not isRunning)) ]
              [ P.string (if isRunning then "Stop" else "Start") ]
          ]
      ]
end

let () =
  match P.find "main" with
  | Some element -> P.render (Clock.make ()) element
  | None -> Js.Console.error "<main> not found!"

AnimationFrame.ml

module P = Preact

let use =
 fun [@preact.hook] (enabled, func) ->
  let[@hook] () =
    P.useEffect
      (fun () ->
        let id = ref None in
        let rec loop t =
          let () = func t in
          let () = id := Some (Webapi.requestCancellableAnimationFrame loop) in
          ()
        in
        let () =
          if enabled
          then id := Some (Webapi.requestCancellableAnimationFrame loop)
          else ()
        in
        Some
          (fun () ->
            match !id with
            | Some id -> Webapi.cancelAnimationFrame id
            | None -> ()))
      (P._1 enabled)
  in
  ()