Skip to content

Commit 76c97c2

Browse files
Add v0.12 rescript-react docs
1 parent 233bf6b commit 76c97c2

26 files changed

+4417
-1
lines changed

data/sidebar_react_v0110.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"Overview": [
3+
"introduction",
4+
"installation",
5+
"migrate-react"
6+
],
7+
"Main Concepts": [
8+
"elements-and-jsx",
9+
"rendering-elements",
10+
"components-and-props",
11+
"arrays-and-keys",
12+
"refs-and-the-dom",
13+
"context",
14+
"styling",
15+
"router"
16+
],
17+
"Hooks & State Management": [
18+
"hooks-overview",
19+
"hooks-effect",
20+
"hooks-state",
21+
"hooks-reducer",
22+
"hooks-context",
23+
"hooks-ref",
24+
"hooks-custom"
25+
],
26+
"Guides": [
27+
"beyond-jsx",
28+
"forwarding-refs",
29+
"extensions-of-props"
30+
]
31+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
---
2+
title: Arrays and Keys
3+
description: "Rendering arrays and handling keys in ReScript and React"
4+
canonical: "/docs/react/latest/arrays-and-keys"
5+
---
6+
7+
# Arrays and Keys
8+
9+
<Intro>
10+
11+
Whenever we are transforming data into an array of elements and put it in our React tree, we need to make sure to give every element an unique identifier to help React distinguish elements for each render. This page will explain the `key` attribute and how to apply it whenever we need to map data to `React.element`s.
12+
13+
</Intro>
14+
15+
## Keys & Rendering Arrays
16+
17+
Keys help React identify which elements have been changed, added, or removed throughout each render. Keys should be given to elements inside the array to give the elements a stable identity:
18+
19+
```res
20+
let numbers = [1, 2, 3, 4, 5];
21+
22+
let items = Belt.Array.map(numbers, (number) => {
23+
<li key={Belt.Int.toString(number)}> {React.int(number)} </li>
24+
})
25+
```
26+
27+
The best way to pick a key is to use a string that uniquely identifies a list item among its siblings. Most often you would use IDs from your data as keys:
28+
29+
```res
30+
type todo = {id: string, text: string}
31+
32+
let todos = [
33+
{id: "todo1", text: "Todo 1"},
34+
{id: "todo2", text: "Todo 2"}
35+
]
36+
37+
let items = Belt.Array.map(todos, todo => {
38+
<li key={todo.id}> {React.string(todo.text)} </li>
39+
})
40+
```
41+
42+
If you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort:
43+
44+
```res {1..3}
45+
let items = Belt.Array.mapWithIndex(todos, (i, todo) => {
46+
// Only do this if items have no stable id
47+
<li key={Belt.Int.toString(i)}>
48+
{todo.text}
49+
</li>
50+
});
51+
```
52+
53+
### Keys Must Only Be Unique Among Siblings
54+
55+
Keys used within arrays should be unique among their siblings. However they don’t need to be globally unique. We can use the same keys when we produce two different arrays:
56+
57+
```res {6,10,17,18,25,27}
58+
type post = {id: string, title: string, content: string}
59+
60+
module Blog = {
61+
@react.component
62+
let make = (~posts: array<post>) => {
63+
let sidebar =
64+
<ul>
65+
{
66+
Belt.Array.map(posts, (post) => {
67+
<li key={post.id}>
68+
{React.string(post.title)}
69+
</li>
70+
})->React.array
71+
}
72+
</ul>
73+
74+
let content = Belt.Array.map(posts, (post) => {
75+
<div key={post.id}>
76+
<h3>{React.string(post.title)}</h3>
77+
<p>{React.string(post.content)}</p>
78+
</div>
79+
});
80+
81+
<div>
82+
{sidebar}
83+
<hr />
84+
{React.array(content)}
85+
</div>
86+
}
87+
}
88+
89+
let posts = [
90+
{id: "1", title: "Hello World", content: "Welcome to learning ReScript & React!"},
91+
{id: "2", title: "Installation", content: "You can install reason-react from npm."}
92+
]
93+
94+
let blog = <Blog posts/>
95+
```
96+
97+
98+
## Rendering `list` Values
99+
100+
In case you ever want to render a `list` of items, you can do something like this:
101+
102+
```res
103+
type todo = {id: string, text: string}
104+
105+
@react.component
106+
let make = () => {
107+
let todoList = list{
108+
{id: "todo1", text: "Todo 1"},
109+
{id: "todo2", text: "Todo 2"},
110+
}
111+
112+
let items =
113+
todoList
114+
->Belt.List.toArray
115+
->Belt.Array.map(todo => {
116+
<li key={todo.id}> {React.string(todo.text)} </li>
117+
})
118+
119+
<div> {React.array(items)} </div>
120+
}
121+
122+
```
123+
124+
We use `Belt.List.toArray` to convert our list to an array before creating our `array<React.element>`. Please note that using `list` has performance impact due to extra conversion costs.
125+
126+
99% of the time you'll want to use arrays (seamless interop, faster JS code), but in some cases it might make sense to use a `list` to leverage advanced pattern matching features etc.
127+
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
---
2+
title: Beyond JSX
3+
description: "Details on how to use ReScript and React without JSX"
4+
canonical: "/docs/react/latest/beyond-jsx"
5+
---
6+
7+
# Beyond JSX
8+
9+
<Intro>
10+
11+
JSX is a syntax sugar that allows us to use React components in an HTML like manner. A component needs to adhere to certain interface conventions, otherwise it can't be used in JSX. This section will go into detail on how the JSX transformation works and what React APIs are used underneath.
12+
13+
</Intro>
14+
15+
**Note:** This section requires knowledge about the low level apis for [creating elements](./elements-and-jsx#creating-elements-from-component-functions), such as `React.createElement` or `ReactDOM.createDOMElementVariadic`.
16+
17+
> **Note:** This page assumes your `bsconfig.json` to be set to `"jsx": { "version": 4 }` to apply the right JSX transformations.
18+
19+
## Component Types
20+
21+
A plain React component is defined as a `('props) => React.element` function. You can also express a component more efficiently with our shorthand type `React.component<'props>`.
22+
23+
Here are some examples on how to define your own component types (often useful when interoping with existing JS code, or passing around components):
24+
25+
```res
26+
// Plain function type
27+
type friend = {name: string, online: bool}
28+
type friendComp = friend => React.element
29+
30+
// Equivalent to
31+
// ({padding: string, children: React.element}) => React.element
32+
type props = {padding: string, children: React.element}
33+
type containerComp = React.component<props>
34+
```
35+
The types above are pretty low level (basically the JS representation of a React component), but since ReScript React has its own ways of defining React components in a more language specific way, let's have a closer look on the anatomy of such a construct.
36+
37+
## JSX Component Interface
38+
39+
A ReScript React component needs to be a (sub-)module with a `make` function and `props` type to be usable in JSX. To make things easier, we provide a `@react.component` decorator to create those functions for you:
40+
41+
<CodeTab labels={["Decorated", "Expanded"]}>
42+
43+
```res
44+
module Friend = {
45+
@react.component
46+
let make = (~name: string, ~children) => {
47+
<div>
48+
{React.string(name)}
49+
children
50+
</div>
51+
}
52+
}
53+
```
54+
```res
55+
module Friend = {
56+
type props<'name, 'children> = {
57+
name: 'name,
58+
children: 'children,
59+
}
60+
61+
let make = ({name, children, _}: props<string, 'children>) => {
62+
ReactDOM.createDOMElementVariadic("div", [{React.string(name)}, children])
63+
}
64+
}
65+
```
66+
67+
</CodeTab>
68+
69+
In the expanded output:
70+
71+
- `props`: A generated record type that has fields according to the labeled arguments of the `make` function
72+
- `make`: A converted `make` function that complies to the component interface `(props) => React.element`
73+
74+
### Special Case React.forwardRef
75+
76+
The `@react.component` decorator also works for `React.forwardRef` calls:
77+
78+
79+
<CodeTab labels={["Decorated", "Expanded"]}>
80+
81+
```res
82+
module FancyInput = {
83+
@react.component
84+
let make = React.forwardRef((~className=?, ~children, ref) =>
85+
<div>
86+
// use ref here
87+
</div>
88+
)
89+
}
90+
```
91+
92+
```res
93+
// Simplified Output
94+
type props<'className, 'children, 'ref> = {
95+
className?: 'className,
96+
children: 'children,
97+
ref?: 'ref,
98+
}
99+
100+
let make = (
101+
{?className, children, _}: props<'className, 'children, ReactRef.currentDomRef>,
102+
ref: Js.Nullable.t<ReactRef.currentDomRef>,
103+
) =>
104+
make(~className, ~children, ~ref, ())
105+
```
106+
107+
</CodeTab>
108+
109+
As shown in the expanded output above, our decorator desugars the function passed to `React.forwardRef` in the same manner as a typical component `make` function. It also creates a `props` type with an optional `ref` field, so we can use it in our JSX call (`<FancyInput ref=.../>`).
110+
111+
So now that we know how the ReScript React component transformation works, let's have a look on how ReScript transforms our JSX constructs.
112+
113+
## JSX Under the Hood
114+
115+
Whenever we are using JSX with a custom component ("capitalized JSX"), we are actually using `React.createElement` to create a new element. Here is an example of a React component without children:
116+
117+
<CodeTab labels={["JSX", "Without JSX", "JS Output"]}>
118+
119+
```res
120+
<Friend name="Fred" age=20 />
121+
```
122+
```res
123+
// classic
124+
React.createElement(Friend.make, {name: "Fred", age:20})
125+
126+
// automatic
127+
React.jsx(Friend.make, {name: "Fred", age: 20})
128+
```
129+
```js
130+
React.createElement(Playground$Friend, { name: "Fred", age: 20 });
131+
```
132+
133+
</CodeTab>
134+
135+
As you can see, it uses `Friend.make` to call the `React.createElement` API. In case you are providing children, it will use `React.createElementVariadic` instead (which is just a different binding for `React.createElement`):
136+
137+
<CodeTab labels={["JSX", "Without JSX", "JS Output"]}>
138+
139+
```res
140+
<Container width=200>
141+
{React.string("Hello")}
142+
{React.string("World")}
143+
</Container>
144+
```
145+
146+
```res
147+
// classic
148+
React.createElementVariadic(
149+
Container.make,
150+
{width: 200, children: React.null},
151+
[{React.string("Hello")}, {React.string("World")}],
152+
)
153+
154+
// automatic
155+
React.jsxs(
156+
Container.make,
157+
{width: 200, children: React.array([{React.string("Hello")}, {React.string("World")}])},
158+
)
159+
```
160+
161+
```js
162+
React.createElement(Container, { width: 200, children: null }, "Hello", "World");
163+
```
164+
165+
</CodeTab>
166+
167+
Note that the `children: React.null` field has no relevance since React will only care about the children array passed as a third argument.
168+
169+
170+
### Dom Elements
171+
172+
"Uncapitalized JSX" expressions are treated as DOM elements and will be converted to `ReactDOM.createDOMElementVariadic` calls:
173+
174+
<CodeTab labels={["JSX", "Without JSX", "JS Output"]}>
175+
176+
```res
177+
<div title="test"/>
178+
```
179+
180+
```res
181+
// classic
182+
ReactDOM.createDOMElementVariadic("div", ~props={title: "test"}, [])
183+
184+
// automatic
185+
ReactDOM.jsx("div", {title: "test"})
186+
```
187+
188+
```js
189+
React.createElement("div", { title: "test" });
190+
```
191+
192+
</CodeTab>
193+
194+
The same goes for uncapitalized JSX with children:
195+
196+
<CodeTab labels={["JSX", "Without JSX", "JS Output"]}>
197+
198+
```res
199+
<div title="test">
200+
<span/>
201+
</div>
202+
```
203+
204+
```res
205+
// classic
206+
ReactDOM.createDOMElementVariadic(
207+
"div",
208+
~props={title: "test"},
209+
[ReactDOM.createDOMElementVariadic("span", [])],
210+
)
211+
212+
// automatic
213+
ReactDOM.jsx("div", {title: "test", children: ?ReactDOM.someElement(ReactDOM.jsx("span", {}))})
214+
```
215+
216+
```js
217+
React.createElement("div", { title: "test" }, React.createElement("span", undefined));
218+
```
219+
220+
</CodeTab>

0 commit comments

Comments
 (0)