summaryrefslogtreecommitdiff
path: root/bs5/server/pages/Comments.re
blob: 6af39ffc25ceb12141806fcb3e46371b6b77e5a8 (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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
module Post = {
  let make = () => {
    <section>
      <p>
        {React.string(
           "Notice how HTML for comments 'streams in' before the JavaScript (or React) has loaded on the page. In fact, the demo is entirely rendered in the server and doesn't use client-side JavaScript at all",
         )}
      </p>
      <p>
        {React.string("This demo is ")}
        <b> {React.string("artificially slowed down")} </b>
        {React.string(" while loading the comments data.")}
      </p>
    </section>;
  };
};

module Data = {
  let delay = 4.0;

  let fakeData = [
    "Wait, it doesn't wait for React to load?",
    "How does this even work?",
    "I like marshmallows",
    "!1!1!1! This is a comment",
    "This is actually static from the server",
    "But, imagine it's dynamic",
  ];

  let get = () => fakeData;

  let cached = ref(false);
  let destroy = () => cached := false;
  let promise = () => {
    cached.contents
      ? Lwt.return(fakeData)
      : {
        let%lwt () = Lwt_unix.sleep(delay);
        cached.contents = true;
        Lwt.return(fakeData);
      };
  };
};

module Comments = {
  [@react.async.component]
  let make = () => {
    let comments = React.Experimental.use(Data.promise());

    Lwt.return(
      <div className="flex gap-4 flex-col">
        {comments
         |> List.mapi((i, comment) =>
              <p
                key={Int.to_string(i)}
                className="font-semibold border-2 border-yellow-200 rounded-lg p-2 bg-yellow-600 text-slate-900">
                {React.string(comment)}
              </p>
            )
         |> React.list}
      </div>,
    );
  };
};

module Page = {
  [@react.component]
  let make = () => {
    <DemoLayout background=Theme.Color.Gray2>
      <main
        className={Theme.text(Theme.Color.Gray11)}
        style={ReactDOM.Style.make(~display="flex", ~marginTop="16px", ())}>
        <article className="flex gap-4 flex-col">
          <h1
            className={Cx.make([
              "text-4xl font-bold ",
              Theme.text(Theme.Color.Gray11),
            ])}>
            {React.string("Rendering React.Suspense on the server")}
          </h1>
          <Post />
          <section>
            <h3
              className={Cx.make([
                "text-2xl font-bold mb-4",
                Theme.text(Theme.Color.Gray11),
              ])}>
              {React.string("Comments")}
            </h3>
            <React.Suspense fallback={<Spinner active=true />}>
              <Comments />
            </React.Suspense>
          </section>
          <h2> {React.string("Thanks for reading!")} </h2>
        </article>
      </main>
    </DemoLayout>;
  };
};

let handler = _request => {
  Dream.stream(
    ~headers=[("Content-Type", "text/html")],
    response_stream => {
      Data.destroy();

      let pipe = data => {
        let%lwt () = Dream.write(response_stream, data);
        Dream.flush(response_stream);
      };

      let%lwt (stream, _abort) =
        ReactDOM.renderToStream(<Document> <Page /> </Document>);

      Lwt_stream.iter_s(pipe, stream);
    },
  );
};