Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c7fcb5a

Browse files
committedJun 29, 2024··
Merge branch 'master' into update-docs-api-29-06
2 parents 0abd086 + cc5fbee commit c7fcb5a

15 files changed

+490
-9
lines changed
 

‎data/sidebar_react_latest.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
"events",
1313
"refs-and-the-dom",
1414
"context",
15+
"memo",
1516
"styling",
1617
"router",
17-
"lazy-components"
18+
"lazy-components",
19+
"import-export-reactjs"
1820
],
1921
"Hooks & State Management": [
2022
"hooks-overview",

‎misc_docs/syntax/decorator_send_pipe.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ keywords: ["send", "pipe", "decorator"]
44
name: "@bs.send.pipe"
55
summary: "This is the `@bs.send.pipe` decorator."
66
category: "decorators"
7+
status: "deprecated"
78
---
89

910
> Removed since compiler version 10.0. Use the [@send](https://rescript-lang.org/docs/manual/latest/bind-to-js-function) decorator instead.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
id: "labeled-argument"
3+
keywords: ["labeled", "argument"]
4+
name: "~arg"
5+
summary: "This is a `labeled argument`."
6+
category: "languageconstructs"
7+
---
8+
9+
When declaring a function, arguments can be prefixed with `~` which means that they can and need to be called by their name, not the argument position. This is especially useful to differentiate them more easily if they are of the same type.
10+
11+
### Example
12+
13+
<CodeTab labels={["ReScript", "JS Output"]}>
14+
15+
```res prelude
16+
let calculateDistance = (~x1, ~y1, ~x2, ~y2) => {
17+
Math.sqrt((x1 -. x2) ** 2. +. (y1 -. y2) ** 2.)
18+
}
19+
20+
calculateDistance(~x1=6., ~y1=8., ~x2=3., ~y2=4.)
21+
```
22+
23+
```js
24+
function calculateDistance(x1, y1, x2, y2) {
25+
return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
26+
}
27+
28+
calculateDistance(6, 8, 3, 4);
29+
```
30+
31+
</CodeTab>
32+
33+
Labeled arguments can be provided in any order:
34+
35+
<CodeTab labels={["ReScript", "JS Output"]}>
36+
37+
```res example
38+
calculateDistance(~x1=6., ~x2=3., ~y1=8., ~y2=4.)
39+
```
40+
41+
```js
42+
calculateDistance(6, 8, 3, 4);
43+
```
44+
45+
</CodeTab>
46+
47+
This also works together with partial application:
48+
49+
<CodeTab labels={["ReScript", "JS Output"]}>
50+
51+
```res example
52+
let calcY = calculateDistance(~x1=6., ~x2=3., ...)
53+
calcY(~y1=8., ~y2=4.)
54+
```
55+
56+
```js
57+
function calcY(none, extra) {
58+
return calculateDistance(6, none, 3, extra);
59+
}
60+
61+
calcY(8, 4);
62+
```
63+
64+
</CodeTab>
65+
66+
### References
67+
68+
* [Labeled Arguments](/docs/manual/latest/function#labeled-arguments)
69+
* [Function Syntax Cheatsheet](/docs/manual/latest/function#tips--tricks)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
---
2+
id: "optional-labeled-argument"
3+
keywords: ["optional", "labeled", "argument"]
4+
name: "~arg=?"
5+
summary: "This is an `optional labeled argument`."
6+
category: "languageconstructs"
7+
---
8+
9+
Labeled arguments, i.e. arguments that are prefixed with `~`, can be suffixed with `=?` to denote that they are optional. Thus, they can be
10+
omitted when calling the function.
11+
12+
### Example
13+
14+
<CodeTab labels={["ReScript", "JS Output"]}>
15+
16+
```res example
17+
let print = (text, ~logLevel=?) => {
18+
switch logLevel {
19+
| Some(#error) => Console.error(text)
20+
| _ => Console.log(text)
21+
}
22+
}
23+
24+
print("An info")
25+
print("An error", ~logLevel=#error)
26+
```
27+
28+
```js
29+
function print(text, logLevel) {
30+
if (logLevel === "error") {
31+
console.error(text);
32+
} else {
33+
console.log(text);
34+
}
35+
}
36+
37+
print("An info", undefined);
38+
39+
print("An error", "error");
40+
```
41+
42+
</CodeTab>
43+
44+
Optional labeled arguments can also hold a default value.
45+
46+
<CodeTab labels={["ReScript", "JS Output"]}>
47+
48+
```res example
49+
let print = (text, ~logLevel=#info) => {
50+
switch logLevel {
51+
| #error => Console.error(text)
52+
| #warn => Console.warn(text)
53+
| #info => Console.log(text)
54+
}
55+
}
56+
57+
print("An info")
58+
print("A warning", ~logLevel=#warn)
59+
```
60+
61+
```js
62+
function print(text, logLevelOpt) {
63+
var logLevel = logLevelOpt !== undefined ? logLevelOpt : "info";
64+
if (logLevel === "warn") {
65+
console.warn(text);
66+
} else if (logLevel === "error") {
67+
console.error(text);
68+
} else {
69+
console.log(text);
70+
}
71+
}
72+
73+
print("An info", undefined);
74+
75+
print("A warning", "warn");
76+
```
77+
78+
</CodeTab>
79+
80+
### References
81+
82+
* [Labeled Arguments](/docs/manual/latest/function#labeled-arguments)
83+
* [Optional Labeled Arguments](/docs/manual/latest/function#optional-labeled-arguments)
84+
* [Labeled Argument with Default Value](/docs/manual/latest/function#optional-with-default-value)
85+
* [Function Syntax Cheatsheet](/docs/manual/latest/function#tips--tricks)

‎pages/docs/manual/latest/import-from-export-to-js.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ canonical: "/docs/manual/latest/import-from-export-to-js"
88

99
You've seen how ReScript's idiomatic [Import & Export](import-export.md) works. This section describes how we work with importing stuff from JavaScript and exporting stuff for JavaScript consumption.
1010

11+
If you're looking for react-specific interop guidance, check out the [React JS Interop guide](../../react/latest/import-export-reactjs.mdx).
12+
1113
**Note**: due to JS ecosystem's module compatibility issues, our advice of keeping your ReScript file's compiled JS output open in a tab applies here **more than ever**, as you don't want to subtly output the wrong JS module import/export code, on top of having to deal with Babel/Webpack/Jest/Node's CommonJS \<-> JavaScript module compatibility shims.
1214

1315
In short: **make sure your bindings below output what you'd have manually written in JS**.

‎pages/docs/manual/latest/let-binding.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,5 +181,5 @@ Still, `%%private` is useful in the following scenarios:
181181

182182
- **Code generators.** Some code generators want to hide some values but it is sometimes very hard or time consuming for code generators to synthesize the types for public fields.
183183

184-
- **Quick prototyping.** During prototyping, we still want to hide some values, but the interface file is not stable yet, `%%private` provide you such convenience.
184+
- **Quick prototyping.** During prototyping, we still want to hide some values, but the interface file is not stable yet. `%%private` provides you such convenience.
185185

‎pages/docs/manual/latest/overview.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ canonical: "/docs/manual/latest/overview"
127127
const myFun = (x, y) => {
128128
const doubleX = x + x;
129129
const doubleY = y + y;
130-
return doubleX + doubleY
130+
return doubleX + doubleY;
131131
};
132132
```
133133
</td>

‎pages/docs/manual/v10.0.0/let-binding.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,5 +181,5 @@ Still, `%%private` is useful in the following scenarios:
181181

182182
- **Code generators.** Some code generators want to hide some values but it is sometimes very hard or time consuming for code generators to synthesize the types for public fields.
183183

184-
- **Quick prototyping.** During prototyping, we still want to hide some values, but the interface file is not stable yet, `%%private` provides you such convenience.
184+
- **Quick prototyping.** During prototyping, we still want to hide some values, but the interface file is not stable yet. `%%private` provides you such convenience.
185185

‎pages/docs/manual/v10.0.0/overview.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ canonical: "/docs/manual/latest/overview"
125125
<pre><code>{`const myFun = (x, y) => {
126126
const doubleX = x + x;
127127
const doubleY = y + y;
128-
return doubleX + doubleY
128+
return doubleX + doubleY;
129129
};`}</code></pre>
130130
</td>
131131
<td>

‎pages/docs/manual/v8.0.0/let-binding.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,5 +217,5 @@ Still, `%private` is useful in the following scenarios:
217217

218218
- Code generators. Some code generators want to hide some values but it is sometimes very hard or time consuming for code generators to synthesize the types for public fields.
219219

220-
- Quick prototyping. During prototyping, we still want to hide some values, but the interface file is not stable yet, `%private` provide you such convenience.
220+
- Quick prototyping. During prototyping, we still want to hide some values, but the interface file is not stable yet. `%private` provides you such convenience.
221221

‎pages/docs/manual/v8.0.0/overview.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ canonical: "/docs/manual/latest/overview"
117117
const myFun = (x, y) => {
118118
const doubleX = x + x;
119119
const doubleY = y + y;
120-
return doubleX + doubleY
120+
return doubleX + doubleY;
121121
};
122122
```
123123
</td>

‎pages/docs/manual/v9.0.0/let-binding.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,5 +181,5 @@ Still, `%private` is useful in the following scenarios:
181181

182182
- Code generators. Some code generators want to hide some values but it is sometimes very hard or time consuming for code generators to synthesize the types for public fields.
183183

184-
- Quick prototyping. During prototyping, we still want to hide some values, but the interface file is not stable yet, `%private` provide you such convenience.
184+
- Quick prototyping. During prototyping, we still want to hide some values, but the interface file is not stable yet. `%private` provides you such convenience.
185185

‎pages/docs/manual/v9.0.0/overview.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ canonical: "/docs/manual/latest/overview"
115115
const myFun = (x, y) => {
116116
const doubleX = x + x;
117117
const doubleY = y + y;
118-
return doubleX + doubleY
118+
return doubleX + doubleY;
119119
};
120120
```
121121
</td>
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
---
2+
title: Import / Export ReactJS
3+
description: "Reusing existing React components"
4+
canonical: "/docs/react/latest/import-export-reactjs"
5+
---
6+
7+
# Import / Export ReactJS
8+
9+
Reusing existing React components in ReScript is straightforward.
10+
This guide will walk you through the steps required to import and use React components within ReScript,
11+
including defining component props and handling various import scenarios.
12+
13+
## Basic Example
14+
15+
To reuse a React component in ReScript, create a new module, specify the component's location, and define its props.
16+
17+
<CodeTab labels={["ReScript", "JS Output"]}>
18+
19+
```res
20+
module Confetti = {
21+
@module("react-confetti") @react.component
22+
external make: (~width: int, ~height: int) => React.element = "default"
23+
}
24+
25+
// Assuming we are in App.res
26+
@react.component
27+
let make = () => {
28+
<Confetti width={300} height={300} />
29+
}
30+
```
31+
32+
```js
33+
import ReactConfetti from "react-confetti";
34+
import * as JsxRuntime from "react/jsx-runtime";
35+
36+
var Confetti = {};
37+
38+
function Playground(props) {
39+
return JsxRuntime.jsx(ReactConfetti, {
40+
width: 300,
41+
height: 300
42+
});
43+
}
44+
```
45+
46+
</CodeTab>
47+
48+
## Importing from Relative Paths
49+
50+
You can import components from relative file paths using the `@module` attribute.
51+
Use "default" to indicate the default export, or specify a named export if needed.
52+
53+
### Named Export Example
54+
55+
<CodeTab labels={["ReScript"]}>
56+
57+
```res
58+
// Equivalent of import { Foo } from "bar"
59+
module Foo = {
60+
@module("bar") @react.component
61+
external make: unit => React.element = "Foo"
62+
}
63+
```
64+
65+
</CodeTab>
66+
67+
## Defining Props Types
68+
69+
You can define a separate type for your component's props within the module.
70+
71+
### Props Type Example
72+
73+
<CodeTab labels={["ReScript", "JS Output"]}>
74+
75+
```res
76+
module Confetti = {
77+
type confettiProps = {
78+
width: int,
79+
height: int,
80+
}
81+
82+
@module("react-confetti") @react.component(: confettiProps)
83+
external make: confettiProps => React.element = "default"
84+
}
85+
86+
@react.component
87+
let make = () => {
88+
<Confetti width={300} height={300} />
89+
}
90+
```
91+
92+
```js
93+
import ReactConfetti from "react-confetti";
94+
import * as JsxRuntime from "react/jsx-runtime";
95+
96+
var Confetti = {};
97+
98+
function Playground(props) {
99+
return JsxRuntime.jsx(ReactConfetti, {
100+
width: 300,
101+
height: 300
102+
});
103+
}
104+
```
105+
106+
</CodeTab>
107+
108+
## Optional Props
109+
110+
To define optional props, use the `?` symbol.
111+
112+
<CodeTab labels={["ReScript", "JS Output"]}>
113+
114+
```res
115+
module Confetti = {
116+
type confettiProps = {
117+
width: int,
118+
height: int,
119+
initialVelocityX?: int,
120+
initialVelocityY?: int,
121+
}
122+
123+
@module("react-confetti") @react.component(: confettiProps)
124+
external make: confettiProps => React.element = "default"
125+
}
126+
127+
@react.component
128+
let make = () => {
129+
<Confetti width={300} height={300} />
130+
}
131+
```
132+
133+
```js
134+
import ReactConfetti from "react-confetti";
135+
import * as JsxRuntime from "react/jsx-runtime";
136+
137+
var Confetti = {};
138+
139+
function Playground(props) {
140+
return JsxRuntime.jsx(ReactConfetti, {
141+
width: 300,
142+
height: 300
143+
});
144+
}
145+
```
146+
147+
</CodeTab>
148+
149+
## Extending Built-in DOM Nodes
150+
151+
To accept existing DOM props for a component, extend the `JsxDOM.domProps` type.
152+
153+
<CodeTab labels={["ReScript", "JS Output"]}>
154+
155+
```res
156+
module Foo = {
157+
type fooProps = {
158+
...JsxDOM.domProps,
159+
customProp: string,
160+
}
161+
162+
@module("foo") @react.component(: fooProps)
163+
external make: fooProps => React.element = "default"
164+
}
165+
166+
@react.component
167+
let make = () => {
168+
<Foo width={"300px"} height={"300px"} customProp="bar" />
169+
}
170+
```
171+
172+
```js
173+
import Foo from "foo";
174+
import * as JsxRuntime from "react/jsx-runtime";
175+
176+
var Foo$1 = {};
177+
178+
function Playground(props) {
179+
return JsxRuntime.jsx(Foo, {
180+
height: "300px",
181+
width: "300px",
182+
customProp: "bar"
183+
});
184+
}
185+
```
186+
187+
</CodeTab>
188+
189+
In this example `width` and `height` can be set because `JsxDOM.domProps` was spread into `fooProps`.

‎pages/docs/react/latest/memo.mdx

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
---
2+
title: memo
3+
description: "Using React.memo"
4+
canonical: "/docs/react/latest/memo"
5+
---
6+
7+
# memo
8+
9+
`React.memo` lets you skip re-rendering a component when its props are unchanged.
10+
11+
Wrap a component in memo to get a memoized version of that component.
12+
This memoized version of your component will usually not be re-rendered when its parent component is re-rendered as long as its props have not changed.
13+
14+
<small>But React may still re-render it: memoization is a performance optimization, not a guarantee.</small>
15+
16+
<CodeTab labels={["ReScript", "JS Output"]}>
17+
18+
```res
19+
@react.component
20+
let make = React.memo((~a: int, ~b: string) => {
21+
<div>
22+
{React.int(a)}
23+
<br />
24+
{React.string(b)}
25+
</div>
26+
})
27+
```
28+
29+
```js
30+
import * as React from "react";
31+
import * as JsxRuntime from "react/jsx-runtime";
32+
33+
var make = React.memo(function (props) {
34+
return JsxRuntime.jsxs("div", {
35+
children: [
36+
props.a,
37+
JsxRuntime.jsx("br", {}),
38+
props.b
39+
]
40+
});
41+
});
42+
```
43+
44+
</CodeTab>
45+
46+
## arePropsEqual
47+
48+
In React, memo can accept an optional argument called "arePropsEqual". This function takes two arguments: the previous props and the new props of the component.
49+
It should return true if the old and new props are the same, meaning the component will produce the same output and behavior with the new props as it did with the old ones.
50+
51+
In ReScript, you can use the `arePropsEqual` function with the `React.memoCustomCompareProps` binding. However, `React.memoCustomCompareProps` cannot be combined with `@react.component`.
52+
53+
To work around this, you can redefine the make binding:
54+
55+
<CodeTab labels={["ReScript", "JS Output"]}>
56+
57+
```res
58+
@react.component
59+
let make = (~disabled, ~onClick) => {
60+
<button
61+
disabled={disabled}
62+
onClick={ev => ev->JsxEvent.Mouse.preventDefault->onClick}>
63+
{React.string("My button")}
64+
</button>
65+
}
66+
67+
let make = React.memoCustomCompareProps(make, (p1, p2) =>
68+
p1.disabled == p2.disabled
69+
)
70+
```
71+
72+
```js
73+
import * as React from "react";
74+
import * as JsxRuntime from "react/jsx-runtime";
75+
76+
function Playground(props) {
77+
var onClick = props.onClick;
78+
return JsxRuntime.jsx("button", {
79+
children: "My button",
80+
disabled: props.disabled,
81+
onClick: (function (ev) {
82+
onClick((ev.preventDefault(), undefined));
83+
})
84+
});
85+
}
86+
87+
var make = React.memo(Playground, (function (p1, p2) {
88+
return p1.disabled === p2.disabled;
89+
}));
90+
```
91+
92+
</CodeTab>
93+
94+
95+
Another approach is to use a custom prop type and remove the `@react.component` annotation.
96+
97+
<CodeTab labels={["ReScript", "JS Output"]}>
98+
99+
```res
100+
type props = {
101+
disabled: bool,
102+
onClick: JsxEvent.Mouse.t => unit,
103+
}
104+
105+
let make = React.memoCustomCompareProps(
106+
({disabled, onClick}) => {
107+
<button disabled={disabled} onClick={ev => ev->onClick}>
108+
{React.string("My button")}
109+
</button>
110+
},
111+
(p1, p2) => p1.disabled == p2.disabled,
112+
)
113+
```
114+
115+
```js
116+
import * as React from "react";
117+
import * as JsxRuntime from "react/jsx-runtime";
118+
119+
var make = React.memo((function (param) {
120+
var onClick = param.onClick;
121+
return JsxRuntime.jsx("button", {
122+
children: "My button",
123+
disabled: param.disabled,
124+
onClick: (function (ev) {
125+
onClick(ev);
126+
})
127+
});
128+
}), (function (p1, p2) {
129+
return p1.disabled === p2.disabled;
130+
}));
131+
```
132+
133+
</CodeTab>

0 commit comments

Comments
 (0)
Please sign in to comment.