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
|
let home = "/";
let demoRenderToStaticMarkup = "/demo/render-to-static-markup";
let demoRenderToString = "/demo/render-to-string";
let demoRenderToStream = "/demo/render-to-stream";
let demoServerOnlyRSC = "/demo/server-only-rsc";
let demoSinglePageRSC = "/demo/single-page-rsc";
let demoRouterRSC = "/demo/router-rsc";
let links = [|
("Server side render to string (renderToString)", demoRenderToString),
(
"Server side render to static markup (renderToStaticMarkup)",
demoRenderToStaticMarkup,
),
("Server side render to stream (renderToStream)", demoRenderToStream),
(
"React Server components without client (createFromFetch)",
demoServerOnlyRSC,
),
(
"React Server components with createFromReadableStream (RSC + SSR)",
demoSinglePageRSC,
),
(
"React Server components with single page router (createFromFetch + createFromReadableStream)",
demoRouterRSC,
),
|];
module Menu = {
[@react.component]
let make = () => {
<ul className="flex flex-col gap-4">
{links
|> Array.map(((title, href)) =>
<li> <Link.WithArrow href> title </Link.WithArrow> </li>
)
|> React.array}
</ul>;
};
};
type location = {
selectedId: option(int),
isEditing: bool,
searchText: option(string),
};
let locationToString = location =>
[
switch (location.selectedId) {
| Some(id) => "selectedId=" ++ Int.to_string(id)
| None => ""
},
"isEditing=" ++ (location.isEditing ? "true" : "false"),
switch (location.searchText) {
| Some(text) => "searchText=" ++ text
| None => ""
},
]
|> List.filter(s => s != "")
|> String.concat("&");
let initialLocation = {
selectedId: None,
isEditing: false,
searchText: None,
};
let locationFromString = str => {
switch (URL.make(str)) {
| Some(url) =>
let searchParams = URL.searchParams(url);
let selectedId =
URL.SearchParams.get(searchParams, "selectedId")
|> Option.map(id => int_of_string(id));
let searchText = URL.SearchParams.get(searchParams, "searchText");
let isEditing =
URL.SearchParams.get(searchParams, "isEditing")
|> Option.map(v =>
switch (v) {
| "true" => true
| "false" => false
| _ => false
}
)
|> Option.value(~default=false);
{
selectedId,
isEditing,
searchText,
};
| None => initialLocation
};
};
type payload = {
body: string,
title: string,
};
/* 'a is melange-fetch's response in melange */
type t('a) = {
location,
navigate: location => unit,
useAction: (string, string) => ((payload, location, unit) => unit, bool),
refresh: option('a) => unit,
};
let useRouter = () => {
location: initialLocation,
navigate: _ => (),
useAction: (_, _) => ((_, _, _) => (), false),
refresh: _ => (),
};
|