Skip to content

Commit b955461

Browse files
mununkicristianoc
authored andcommitted
update JSX page
1 parent c42fce1 commit b955461

File tree

1 file changed

+66
-89
lines changed

1 file changed

+66
-89
lines changed

pages/docs/manual/latest/jsx.mdx

Lines changed: 66 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,19 @@ Would you like some HTML syntax in your ReScript? If not, quickly skip over this
1010

1111
ReScript supports the JSX syntax, with some slight differences compared to the one in [ReactJS](https://facebook.github.io/react/docs/introducing-jsx.html). ReScript JSX isn't tied to ReactJS; they translate to normal function calls:
1212

13-
**Note** for [ReasonReact](https://reasonml.github.io/reason-react/en/) readers: this isn't what ReasonReact turns JSX into, in the end. See Usage section for more info.
13+
**Note** for [ReScriptReact](https://rescript-lang.org/docs/react/latest/introduction) readers: this isn't what ReScriptReact turns JSX into, in the end. See Usage section for more info.
1414

15-
## Capitalized Tag
15+
## Capitalized
1616

1717
<CodeTab labels={["ReScript", "JS Output"]}>
1818

1919
```res
2020
<MyComponent name={"ReScript"} />
2121
```
2222
```js
23-
React.createElement(
24-
MyComponent.make,
25-
MyComponent.makeProps("ReScript", undefined)
26-
);
23+
React.createElement(MyComponent, {
24+
name: "ReScript",
25+
});
2726
```
2827

2928
</CodeTab>
@@ -33,18 +32,17 @@ becomes
3332
<CodeTab labels={["ReScript", "JS Output"]}>
3433

3534
```res
36-
@JSX MyComponent.createElement(~name="ReScript", ~children=list{}, ())
35+
MyComponent.createElement(~name="ReScript", ~children=list{}, ())
3736
```
3837
```js
39-
React.createElement(
40-
MyComponent.make,
41-
MyComponent.makeProps("ReScript", undefined)
42-
);
38+
React.createElement(MyComponent, {
39+
name: "ReScript",
40+
});
4341
```
4442

4543
</CodeTab>
4644

47-
## Uncapitalized Tag
45+
## Uncapitalized
4846

4947
<CodeTab labels={["ReScript", "JS Output"]}>
5048

@@ -64,7 +62,7 @@ becomes
6462
<CodeTab labels={["ReScript", "JS Output"]}>
6563

6664
```res
67-
@JSX div(~onClick=handler, ~children=list{child1, child2}, ())
65+
div(~onClick=handler, ~children=list{child1, child2}, ())
6866
```
6967
```js
7068
React.createElement("div", {
@@ -92,7 +90,7 @@ becomes
9290
<CodeTab labels={["ReScript", "JS Output"]}>
9391

9492
```res
95-
@JSX list{child1, child2}
93+
list{child1, child2}
9694
```
9795
```js
9896
React.createElement(React.Fragment, undefined, child1, child2);
@@ -108,153 +106,132 @@ React.createElement(React.Fragment, undefined, child1, child2);
108106
<MyComponent> child1 child2 </MyComponent>
109107
```
110108
```js
111-
React.createElement(MyComponent.make, MyComponent.makeProps(null, undefined), child1, child2);
109+
React.createElement(MyComponent, { children: null }, child1, child2);
112110
```
113111

114112
</CodeTab>
115113

116-
This is the syntax for passing a list of two items, `child1` and `child2`, to the children position. It desugars to a list containing `child1` and `child2`:
114+
This is the syntax for passing a list of two items, `child1` and `child2`, to the children position. It transforms to a list containing `child1` and `child2`:
117115

118116
<CodeTab labels={["ReScript", "JS Output"]}>
119117

120118
```res
121-
@JSX MyComponent.createElement(~children=list{child1, child2}, ())
119+
MyComponent.createElement(~children=list{child1, child2}, ())
122120
```
123121
```js
124122
React.createElement(MyComponent.make, MyComponent.makeProps(null, undefined), child1, child2);
125123
```
126124

127125
</CodeTab>
128126

129-
**Note** again that this isn't the transform for ReasonReact; ReasonReact turns the final list into an array. But the idea still applies.
127+
**Note** again that this isn't the transform for ReScriptReact; ReScriptReact turns the final list into an array. But the idea still applies.
128+
129+
So naturally, `<MyComponent> myChild </MyComponent>` is transformed to `MyComponent.createElement(~children=list{myChild}, ())`. I.e. whatever you do, the arguments passed to the children position will be wrapped in a list.
130130

131-
So naturally, `<MyComponent> myChild </MyComponent>` desugars to `@JSX MyComponent.createElement(~children=list{myChild}, ())`. I.e. whatever you do, the arguments passed to the children position will be wrapped in a list. What if you don't want that? **What if you want to directly pass `myChild` without an extra wrapping**?
131+
## Usage
132132

133-
#### Children Spread
133+
See [ReScriptReact Elements & JSX](https://rescript-lang.org/docs/react/latest/elements-and-jsx) for an example application of JSX, which transforms the above calls into a ReScriptReact-specific call.
134134

135-
To solve the above problem, we've introduced
135+
Here's a JSX tag that shows most of the features.
136136

137137
<CodeTab labels={["ReScript", "JS Output"]}>
138138

139139
```res
140-
<MyComponent> ...myChild </MyComponent>
140+
<MyComponent
141+
booleanAttribute={true}
142+
stringAttribute="string"
143+
intAttribute=1
144+
forcedOptional=?{Some("hello")}
145+
onClick={handleClick}>
146+
<div> {React.string("hello")} </div>
147+
</MyComponent>
141148
```
142149
```js
143-
React.createElement(MyComponent.make, MyComponent.makeProps(myChild, undefined));
150+
React.createElement(MyComponent, {
151+
children: React.createElement("div", undefined, "hello"),
152+
booleanAttribute: true,
153+
stringAttribute: "string",
154+
intAttribute: 1,
155+
forcedOptional: "hello",
156+
onClick: handleClick
157+
});
144158
```
145159

146160
</CodeTab>
147161

148-
This passes the value `myChild` _without_ wrapping it in a list (or array, in the case of ReasonReact). Aka, this desugars to:
162+
## Departures From JS JSX
163+
164+
- Attributes and children don't mandate `{}`, but we show them anyway for ease of learning. Once you format your file, some of them go away and some turn into parentheses.
165+
- The spread props is supported, but there are some restrictions.
166+
- Punning!
167+
168+
### Spread props
169+
170+
The JSX spread props is supported.
149171

150172
<CodeTab labels={["ReScript", "JS Output"]}>
151173

152174
```res
153-
@JSX MyComponent.createElement(~children=myChild, ())
175+
<Comp {...props} a="a" />
154176
```
155177
```js
156-
React.createElement(MyComponent.make, MyComponent.makeProps(myChild, undefined));
178+
React.createElement(Comp, {
179+
a: "a",
180+
b: "b"
181+
});
157182
```
158183

159184
</CodeTab>
160185

161-
This is extra useful in the cases where you are handled `myChild` that is already a list of things, and want to forward that without wrapping it an extra time (which would be a type error) \*. It also allows you to pass arbitrary data structures at `children` position (remember, JSX `children` is really just a totally normal prop):
186+
The multiple spreads are not allowed:
162187

163-
<CodeTab labels={["ReScript", "JS Output"]}>
188+
<CodeTab labels={["ReScript"]}>
164189

165190
```res
166-
<MyComponent> ...((theClassName) => <div className=theClassName />) </MyComponent>
167-
168-
<MyForm> ...("Hello", "Submit") </MyForm>
169-
```
170-
```js
171-
React.createElement(
172-
make,
173-
makeProps(function (theClassName) {
174-
return React.createElement("div", {
175-
className: theClassName,
176-
});
177-
}, undefined)
178-
);
179-
180-
React.createElement(MyForm.make, MyForm.makeProps(["Hello", "Submit"], undefined));
191+
<NotAllowed {...props1} {...props2} />
181192
```
182193

183194
</CodeTab>
184195

185-
## Usage
186-
187-
See [ReasonReact JSX](https://reasonml.github.io/reason-react/docs/en/jsx) for an example application of JSX, which transforms the above calls into a ReasonReact-specific call.
188-
189-
Here's a JSX tag that shows most of the features.
196+
The spread must be the first position followed by other props:
190197

191-
<CodeTab labels={["ReScript", "JS Output"]}>
198+
<CodeTab labels={["ReScript"]}>
192199

193200
```res
194-
<MyComponent
195-
booleanAttribute={true}
196-
stringAttribute="string"
197-
intAttribute=1
198-
forcedOptional=?{Some("hello")}
199-
onClick={handleClick}>
200-
<div> {React.string("hello")} </div>
201-
</MyComponent>
202-
```
203-
```js
204-
React.createElement(
205-
MyComponent.make,
206-
MyComponent.makeProps(
207-
true,
208-
"string",
209-
1,
210-
"hello",
211-
handleClick,
212-
React.createElement("div", undefined, "hello"),
213-
undefined
214-
)
215-
);
201+
<NotAllowed a="a" {...props} />
216202
```
217203

218204
</CodeTab>
219205

220-
## Departures From JS JSX
221-
222-
- Attributes and children don't mandate `{}`, but we show them anyway for ease of learning. Once you format your file, some of them go away and some turn into parentheses.
223-
- There is no support for JSX prop spread: `<Comp {...props} />`. Though somewhat related, we do have children spread, described above: `<Comp> ...children </Comp>`.
224-
- Punning!
225-
226206
### Punning
227207

228208
"Punning" refers to the syntax shorthand for when a label and a value are the same. For example, in JavaScript, instead of doing `return {name: name}`, you can do `return {name}`.
229209

230-
Reason JSX supports punning. `<input checked />` is just a shorthand for `<input checked=checked />`. The formatter will help you format to the punned syntax whenever possible. This is convenient in the cases where there are lots of props to pass down:
210+
JSX supports punning. `<input checked />` is just a shorthand for `<input checked=checked />`. The formatter will help you format to the punned syntax whenever possible. This is convenient in the cases where there are lots of props to pass down:
231211

232212
<CodeTab labels={["ReScript", "JS Output"]}>
233213

234214
```res
235215
<MyComponent isLoading text onClick />
236216
```
237217
```js
238-
React.createElement(
239-
MyComponent.make,
240-
MyComponent.makeProps(isLoading, text, onClick, undefined)
241-
);
218+
React.createElement(MyComponent, {
219+
isLoading: true,
220+
text: text,
221+
onClick: onClick
222+
});
242223
```
243224

244225
</CodeTab>
245226

246-
Consequently, a Reason JSX component can cram in a few more props before reaching for extra libraries solutions that avoids props passing.
227+
Consequently, a JSX component can cram in a few more props before reaching for extra libraries solutions that avoids props passing.
247228

248229
**Note** that this is a departure from ReactJS JSX, which does **not** have punning. ReactJS' `<input checked />` desugars to `<input checked=true />`, in order to conform to DOM's idioms and for backward compatibility.
249230

250231
## Tip & Tricks
251232

252-
For library authors wanting to take advantage of the JSX: the `@JSX` attribute above is a hook for potential ppx macros to spot a function wanting to format as JSX. Once you spot the function, you can turn it into any other expression.
233+
For library authors wanting to take advantage of the JSX: the `@JSX` attribute is a hook for potential ppx macros to spot a function wanting to format as JSX. Once you spot the function, you can turn it into any other expression.
253234

254-
This way, everyone gets to benefit the JSX syntax without needing to opt into a specific library using it, e.g. ReasonReact.
235+
This way, everyone gets to benefit the JSX syntax without needing to opt into a specific library using it, e.g. ReScriptReact.
255236

256237
JSX calls supports the features of [labeled arguments](function.md#labeled-arguments): optional, explicitly passed optional and optional with default.
257-
258-
## Design Decisions
259-
260-
\* You might wonder why you never needed such children spread in ReactJS; ReactJS uses some special runtime logic + special syntax transforms + variadic argument detection & marking to avoid most of these cases ([see here](https://github.com/facebook/react/blob/9b36df86c6ccecb73ca44899386e6a72a83ad445/packages/react/src/ReactElement.js#L207)). Such dynamic usage complexifies the type system detection _quite a bit_. Since we control the whole syntax and ReasonReact, we decided to introduce children spread to disambiguate between the case of wrapping vs not wrapping, without compile-time & runtime cost!

0 commit comments

Comments
 (0)