package server-reason-react
Install
dune-project
Dependency
Authors
Maintainers
Sources
sha256=7811cd16a7256edbebd06057072142fc2fa1d81de784442e21f3225f06f08ce2
sha512=d60084b34f4086bc401f5f1e209714ab297b5dd94b9b55050816ba9dd0579b2c88745b1813ab57d9584c826af9602df279e8ecfdc04cde62f94d1fec9506dd45
doc/browser_ppx.html
Exclude client code from the native build
browser_only is the ppx to exclude client code from the server build and conditionally execute code based on each platform.
For example, if you're using Webapi to query the DOM and extract some data from it. This code should only run on the client, and there's no equivalent or fallback on the server.
The ppx expose the [%browser_only] extension and [@browser_only] attribute that can be used to discard functions and values, and [switch%platform] to conditionally compile and execute code based on the platform.
Example
let%browser_only countDomNodes = (id) => {
let elements = Webapi.Element.querySelector("#" ++ id);
let arr_elements = Webapi.Element.toArray(elements);
Array.length(arr_elements);
}switch%platform (Runtime.platform) {
| Server => print_endline("This prints to the terminal")
| Client => Js.log("This prints to the console")
};Installation
Add server-reason-react.browser_ppx into to your pps field under a dune stanzas (melange.emit, libraries or executable) in your dune files.
You would need to add it on both "server" and "client" dune files. Adding the -js flag server-reason-react.browser_ppx -js for the client and without the flag for the server:
; server exectuable
(executable
(name server)
(preprocess
(pps server-reason-react.browser_ppx)))
; melange emitting JavaScript
(melange.emit
(target app)
(preprocess
(pps server-reason-react.browser_ppx -js)))Usage
let%browser_only to discard functions
let%browser_only countDomNodes = (id) => {
let elements = Webapi.Element.querySelector("#" ++ id);
let arr_elements = Webapi.Element.toArray(elements);
Array.length(arr_elements);
};The method tagged by browser_only and it will keep the function for the client build, but will be discarded for the server build. On the server build, the ppx transforms the body of function into a Runtime.Impossible_in_ssr exception.
If this function ever runs on the server accidentally, it will raise the exception. If this exception isn't caught, the server will obviously crash. This situation is very unlikely to happen, but in case of not being sure, it's good to be prepared for it and add a try catch block.
There may be other cases where catching the exception might be useful. For example, if you want to provide a default value or a fallback.
Following with the example from above:
let%browser_only countDomNodes = (id) => {
let elements = Webapi.Element.querySelector("#" ++ id);
let arr_elements = Webapi.Element.toArray(elements);
Array.length(arr_elements);
}
let main = id =>
switch (countDomNodes(id)) {
| exception Runtime.Impossible_in_ssr(_message) => 0
};Now, the function main will return 0 if the function countDomNodes raises the Runtime.Impossible_in_ssr exception, and is "safe" (as in, it won't crash) to run on the server.
switch%platform to conditionally execute code based on the platform
switch%platform allows to conditionally execute code based on the platform. There are some cases where you need to run a specific code only on the server or only on the client.
An example is worth a thousand words:
switch%platform (Runtime.platform) {
| Server => print_endline("This prints to the terminal")
| Client => Js.log("This prints to the console")
};Because Reason (and also OCaml) is a language where everything is an expression, not only can execute code, but any expression can be part of the switch.
let howManyColumns =
switch%platform (Runtime.platform) {
| Server => 0
| Client => 12
};Note that the expression is evaluated for each platform, but the type needs to be the same for all the branches.
[@platform] attribute
The [@platform] attribute allows to specify code blocks that should only be included in the JavaScript or native build, respectively. The [@platform] attribute works the same way as the [switch%platform], but applied to entire modules.
Again, this is useful when you have code that is specific to one platform and should not be included in the other, but all packaged into a single module.
For example, you can define two modules, but only one of them should be kept in the final build based on the platform.
[@platform js]
module X = {
type t = Js.Json.t;
let a = 2 + 2;
};
[@platform native]
module Y = {
type t = Js.Json.t;
let a = 4 + 4;
};When compiling with the `-js` flag, only the block with [@platform js] (module X) is kept, and when compiling without it, only the block with [@platform native] (module Y) is kept.
If you name the modules the same, the compiler won't complain, since you would get a single module available in both targets, respectively.
[@platform js]
module X = {
type t = Js.Json.t;
let a = 2 + 2;
};
[@platform native]
module X = {
type t = Yojson.Basic.t;
let a = 4 + 4;
};