Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 40 additions & 28 deletions docs/jsx.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@
title: JSX
---

Reason comes with the [JSX](https://reasonml.github.io/docs/en/jsx.html) syntax! ReasonReact works very similar to how [the ReactJS JSX transform](https://reactjs.org/docs/introducing-jsx.html) does.
Reason comes with [JSX](https://reasonml.github.io/docs/en/jsx.html) syntax. Enables representation of HTML-like expressions within the language.

reason-react enables [the ReactJS JSX transform](https://reactjs.org/docs/introducing-jsx.html) in Reason.

Since `reason-react.0.12.0`, the JSX transformation currently supports the [New JSX Transform](https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html). JSX functions are imported from `react/jsx-runtime`. Previous versions of reason-react used the legacy API `React.createElement`.

# Install

To use it, you would need to install [`reason-react-ppx`](https://opam.ocaml.org/packages/reason-react-ppx/) and add `(preprocess (pps reason-react-ppx))` in [`melange.emit or library`](https://dune.readthedocs.io/en/stable/melange.html) stanzas in your `dune` file.

Here's a list of transformations made by the [ppx](https://ocaml.org/docs/metaprogramming):
# What the ppx does

Here's a list of transformations made by the [ppx](https://ocaml.org/docs/metaprogramming).

## Uncapitalized

Expand All @@ -17,27 +25,26 @@ Here's a list of transformations made by the [ppx](https://ocaml.org/docs/metapr
transforms into

```reason
ReactDOM.createDOMElementVariadic(
ReactDOM.jsxs(
"div",
~props=ReactDOM.domProps(~foo=bar, ()),
[|child1, child2|]
);
ReactDOM.domProps(
~children=React.array([|child1, child2|]),
~foo=bar,
(),
)
)
```

which compiles to the JavaScript code:

```js
React.createElement('div', {foo: bar}, child1, child2)
React.jsx('div', {foo: bar, children: [ child1, child2 ] })
```

Prop-less `<div />` transforms into

```reason
ReactDOM.createDOMElementVariadic(
"div",
~props=ReactDOM.domProps(),
[||]
);
ReactDOM.jsx("div", ReactDOM.domProps());
```

Which compiles to
Expand All @@ -49,80 +56,85 @@ React.createElement('div', {})
## Capitalized

```reason
<MyReasonComponent key={a} ref={b} foo={bar} baz={qux}> {child1} {child2} </MyReasonComponent>
<MyReasonComponent ref={b} foo={bar} baz={qux}> {child1} {child2} </MyReasonComponent>
```

transforms into

```reason
React.createElementVariadic(
React.jsxs(
MyReasonComponent.make,
MyReasonComponent.makeProps(
~key=a,
~ref=b,
~foo=bar,
~baz=qux,
~children=React.null,
~children=[|child1, child2|],
()
),
[|child1, child2|]
);
```

which compiles to

```js
React.createElement(
React.jsxs(
MyReasonComponent.make,
{
key: a,
ref: b,
foo: bar,
baz: qux,
children: null,
children: [ child1, child2 ],
},
child1,
child2,
);
```

Prop-less `<MyReasonComponent />` transforms into

```reason
React.createElement(MyReasonComponent.make, MyReasonComponent.makeProps());
React.jsx(
MyReasonComponent.make,
MyReasonComponent.makeProps(),
);
```

which compiles to

```js
React.createElement(MyReasonComponent.make, {});
React.jsx(MyReasonComponent.make, {});
```

The `make` above is exactly the same `make` function you've seen in the previous section.

`ref` and `key` are reserved in ReasonReact, just like in ReactJS. **Don't** use them as props in your component!
`ref` and `key` are reserved in reason-react, just like in ReactJS. **Don't** use them as props in your component!

## Fragment

Fragment lets you group elements without a wrapper node, and return a single element without any effect on the DOM. More details about this in the [react documentation: Fragments](https://react.dev/reference/react/Fragment).

The empty JSX tag `<></>` is shorthand for `<React.Fragment></React.Fragment>`

```reason
<> child1 child2 </>;
```

transforms into

```reason
ReactDOMRe.createElement(ReasonReact.fragment, [|child1, child2|]);
React.jsx(
React.jsxFragment,
ReactDOM.domProps(~children=React.array([|child1, child2|]), ()),
);
```

Which compiles to

```js
React.createElement(React.Fragment, undefined, child1, child2);
React.jsx(React.Fragment, { children: [child1, child2] });
```

## Children

ReasonReact children are **fully typed**, and you can pass any data structure to it (as long as the receiver component permits it). When you write:
reason-react children are **fully typed**, and you can pass any data structure to it (as long as the receiver component permits it). When you write:

```reason
<MyReasonComponent> <div /> <div /> </MyReasonComponent>
Expand Down
4 changes: 2 additions & 2 deletions dune-project
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
(name reason-react)
(synopsis "Reason bindings for React.js")
(description
"ReasonReact helps you use Reason to build React components with deeply integrated, strong, static type safety.\n\nIt is designed and built by people using Reason and React in large, mission critical production React codebases.")
"reason-react helps you use Reason to build React components with deeply integrated, strong, static type safety.\n\nIt is designed and built by people using Reason and React in large, mission critical production React codebases.")
(depends
ocaml
(melange (>= 3.0.0))
Expand All @@ -53,7 +53,7 @@
(package
(name reason-react-ppx)
(synopsis "React.js JSX PPX")
(description "ReasonReact JSX PPX")
(description "reason-react JSX PPX")
(depends
(ocaml
(>= 4.14))
Expand Down
2 changes: 1 addition & 1 deletion reason-react-ppx.opam
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# This file is generated by dune, edit dune-project instead
opam-version: "2.0"
synopsis: "React.js JSX PPX"
description: "ReasonReact JSX PPX"
description: "reason-react JSX PPX"
maintainer: [
"David Sancho <[email protected]>"
"Antonio Monteiro <[email protected]>"
Expand Down
2 changes: 1 addition & 1 deletion reason-react.opam
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
opam-version: "2.0"
synopsis: "Reason bindings for React.js"
description: """
ReasonReact helps you use Reason to build React components with deeply integrated, strong, static type safety.
reason-react helps you use Reason to build React components with deeply integrated, strong, static type safety.

It is designed and built by people using Reason and React in large, mission critical production React codebases."""
maintainer: [
Expand Down
12 changes: 11 additions & 1 deletion src/React.re
Original file line number Diff line number Diff line change
Expand Up @@ -723,7 +723,9 @@ external useContext: Context.t('any) => 'any = "useContext";
[@mel.module "react"] external useRef: 'value => ref('value) = "useRef";
[@mel.module "react"] external useId: unit => string = "useId";

[@mel.module "react"] external useDeferredValue: 'a => 'a = "useDeferredValue";
[@mel.module "react"]
external useDeferredValue: ('a, ~initialValue: 'a=?) => 'a =
"useDeferredValue";

[@mel.module "react"]
external useImperativeHandle0:
Expand Down Expand Up @@ -877,6 +879,11 @@ module Uncurried = {
external useTransition: unit => (bool, callback(callback(unit, unit), unit)) =
"useTransition";

[@mel.module "react"]
external useTransitionAsync:
unit => (bool, callbackAsync(callback(unit, unit), unit)) =
"useTransition";

[@mel.module "react"]
external startTransition: ([@mel.uncurry] (unit => unit)) => unit =
"startTransition";
Expand All @@ -887,12 +894,15 @@ external useDebugValue: ('value, ~format: 'value => string=?, unit) => unit =

[@mel.module "react"]
external act: (unit => unit) => Js.Promise.t(unit) = "act";

[@mel.module "react"]
external actAsync: (unit => Js.Promise.t(unit)) => Js.Promise.t(unit) =
"act";

module Experimental = {
/* This module is used to bind to APIs for future versions of React. There is no guarantee of backwards compatibility or stability. */
external promise: Js.Promise.t(element) => element = "%identity";

/* https://react.dev/reference/react/use */
[@mel.module "react"] external usePromise: Js.Promise.t('a) => 'a = "use";
[@mel.module "react"] external useContext: Context.t('a) => 'a = "use";
Expand Down
12 changes: 11 additions & 1 deletion src/React.rei
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,9 @@ external useContext: Context.t('any) => 'any = "useContext";
[@mel.module "react"] external useRef: 'value => ref('value) = "useRef";
[@mel.module "react"] external useId: unit => string = "useId";

[@mel.module "react"] external useDeferredValue: 'a => 'a = "useDeferredValue";
[@mel.module "react"]
external useDeferredValue: ('a, ~initialValue: 'a=?) => 'a =
"useDeferredValue";

[@mel.module "react"]
external useImperativeHandle0:
Expand Down Expand Up @@ -573,14 +575,22 @@ external startTransition: ([@mel.uncurry] (unit => unit)) => unit =
external useTransition: unit => (bool, callback(callback(unit, unit), unit)) =
"useTransition";

[@mel.module "react"]
external useTransitionAsync:
unit => (bool, callbackAsync(callback(unit, unit), unit)) =
"useTransition";

[@mel.module "react"]
external act: (unit => unit) => Js.Promise.t(unit) = "act";

[@mel.module "react"]
external actAsync: (unit => Js.Promise.t(unit)) => Js.Promise.t(unit) =
"act";

module Experimental: {
/* This module is used to bind to APIs for future versions of React. There is no guarantee of backwards compatibility or stability. */
external promise: Js.Promise.t(element) => element = "%identity";

[@mel.module "react"] external usePromise: Js.Promise.t('a) => 'a = "use";
[@mel.module "react"] external useContext: Context.t('a) => 'a = "use";

Expand Down