I just finished a rewrite (up-to-date??) of Todo web app. It uses js_of_ocaml, react(opam react), reactiveData, dune, ocaml 4.10.0 and good old stdlib. Mostly, just good old standard tools that comes with ocaml and js_of_ocaml compiler.
One aspect of the codebase is that I have tried to incorporate component based implementation rather than the strict MVC implementation.
I am announcing it here in the hope that those new to ocaml, js_of_ocaml and in doing web app in ocaml can perhaps use it as another specimen to their learning journey.
Mostly to get around circular dependency. i.e. I define action type in jsoo_todomvc.ml which is the topmost/root module, i.e. not other modules depend on it but it itself depends on other modules such as Footer, Newtodo and so on. That type (action) is used in other components/modules such as new_todo.ml and footer.ml which makes it circular, i.e. module Jsoo_todomvc depends on New_todo and New_todomvc depends on Jsoo_todomvc which is not allowed in ocaml - as it shouldn’t. Using polymorphic variant breaks this circular dependency. Perhaps this wasn’t the intended purpose of polymorphic variants but it is quite effective in this scenario.
You can get around circular modular dependency via recursive modules but in this instance it felt bit heavy since using recursive module you have to restructure your source code in a certain way,i.e define your dependent modules in the same file.
Another alternative to get around this circular dependency issue is to define the type in std.ml so you can use normal variant. However, I went ahead with polymorphic variant in this instance as they are quite flexible as compared to normal variants.
I would suggest that the action signal is better represented as an event stream. In that case the dispatch function simplifies to dispatch : action -> unit, instead of dispatch : action option -> unit.
In the end I went with (2) because 1 is more work but also because it seemed semantically more correct and cogent for action type to not have None action.
All actions dispatched in the app do something. The only time the action signal is None is at the very beginning when you create the signal. But with an event stream, initially there is no event:
let action_s, dispatch = React.S.create None in
let action_e, dispatch = React.E.create () in