summaryrefslogtreecommitdiff
path: root/bs5/client/RouterRSC.re
blob: 745c22deec1821353f474d94960f0fd154a28464 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
module DOM = Webapi.Dom;
module Location = DOM.Location;
module History = DOM.History;
module ReadableStream = Webapi.ReadableStream;

[@mel.scope "window"] [@mel.set]
external setNavigate: (Webapi.Dom.Window.t, string => unit) => unit =
  "__navigate";

[@mel.module "react"]
external startTransition: (unit => unit) => unit = "startTransition";
external readable_stream: ReadableStream.t =
  "window.srr_stream.readable_stream";

let fetchApp = url => {
  let headers =
    Fetch.HeadersInit.make({"Accept": "application/react.component"});
  Fetch.fetchWithInit(
    url,
    Fetch.RequestInit.make(~method=Fetch.Get, ~headers, ()),
  );
};

module App = {
  let initialData =
    ReactServerDOMEsbuild.createFromReadableStream(readable_stream);

  [@react.component]
  let make = () => {
    let initialElement = React.Experimental.use(initialData);
    let (data, setData) = React.Uncurried.useState(() => initialElement);

    let navigate = search => {
      let location = DOM.window->DOM.Window.location;
      let currentSearch = Location.search(location);
      if (currentSearch == "?" ++ search) {
        ();
      } else {
        let origin = Location.origin(location);
        let pathname = Location.pathname(location);
        let currentURL = origin ++ pathname;
        let url = URL.makeExn(currentURL)->URL.setSearchAsString(search);
        let app = fetchApp(URL.toString(url));
        let element = ReactServerDOMEsbuild.createFromFetch(app);
        startTransition(() => {
          setData(. _ => element);
          History.pushState(
            History.state(DOM.history),
            "",
            URL.toString(url),
            DOM.history,
          );
        });
        ();
      };
    };

    /* Publish navigate fn into window.__navigate */
    setNavigate(Webapi.Dom.window, navigate);

    <ReasonReactErrorBoundary
      fallback={error => {
        Js.log(error);
        <h1> {React.string("Something went wrong")} </h1>;
      }}>
      data
    </ReasonReactErrorBoundary>;
  };
};

let document: option(Webapi.Dom.Element.t) = [%mel.raw "window.document"];

let body =
  Webapi.Dom.document
  ->Webapi.Dom.Document.asHtmlDocument
  ->Option.bind(Webapi.Dom.HtmlDocument.body);

switch (document) {
| Some(element) =>
  startTransition(() => {
    let _ = ReactDOM.Client.hydrateRoot(element, <App />);
    ();
  })
| None => Js.log("Root element not found")
};