diff --git a/pages/docs/react/latest/arrays-and-keys.mdx b/pages/docs/react/latest/arrays-and-keys.mdx index f7b46621e..869804f28 100644 --- a/pages/docs/react/latest/arrays-and-keys.mdx +++ b/pages/docs/react/latest/arrays-and-keys.mdx @@ -16,7 +16,9 @@ Whenever we are transforming data into an array of elements and put it in our Re Arrays require a special function `React.array` to convert an `array` to render as `Jsx.element`. -```res + + +```res example type todo = {id: string, text: string} @react.component @@ -31,21 +33,68 @@ let make = () => { } ``` +```js +function Playground(props) { + var todos = [ + { + id: "todo1", + text: "Todo 1" + }, + { + id: "todo2", + text: "Todo 2" + } + ]; + var items = todos.map(function (todo) { + return JsxRuntime.jsx("li", { + children: todo.text + }, todo.id); + }); + return JsxRuntime.jsx("ul", { + children: items + }); +} +``` + + + ## Keys 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: -```res -let numbers = [1, 2, 3, 4, 5]; + + +```res example +let numbers = [1, 2, 3, 4, 5] let items = Array.map(numbers, (number) => {
  • {React.int(number)}
  • }) ``` +```js +var numbers = [ + 1, + 2, + 3, + 4, + 5 +]; + +var items = numbers.map(function (number) { + return JsxRuntime.jsx("li", { + children: number + }, number.toString()); + }); +``` + +
    + 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: -```res + + +```res prelude type todo = {id: string, text: string} let todos = [ @@ -58,22 +107,58 @@ let items = Array.map(todos, todo => { }) ``` +```js +var todos = [ + { + id: "todo1", + text: "Todo 1" + }, + { + id: "todo2", + text: "Todo 2" + } +]; + +var items = todos.map(function (todo) { + return JsxRuntime.jsx("li", { + children: todo.text + }, todo.id); + }); +``` + + + If you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort: -```res {1..3} -let items = Array.mapWithIndex(todos, (i, todo) => { + + +```res example {1..3} +let items = Array.mapWithIndex(todos, (todo, i) => { // Only do this if items have no stable id
  • - {todo.text} + {React.string(todo.text)}
  • -}); +}) ``` +```js +var items = todos.map(function (todo, i) { + return JsxRuntime.jsx("li", { + children: todo.text + }, i.toString()); + }); +``` + +
    + + ### Keys Must Only Be Unique Among Siblings 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: -```res {6,10,17,18,25,27} + + +```res example {6,10,17,18,25,27} type post = {id: string, title: string, content: string} module Blog = { @@ -81,23 +166,19 @@ module Blog = { let make = (~posts: array) => { let sidebar =
      - { - Array.map(posts, (post) => { -
    • - {React.string(post.title)} -
    • - })->React.array - } + {Array.map(posts, post => { +
    • {React.string(post.title)}
    • + })->React.array}
    - let content = Array.map(posts, (post) => { -
    -

    {React.string(post.title)}

    -

    {React.string(post.content)}

    -
    - }); - -
    + let content = Array.map(posts, post => { +
    +

    {React.string(post.title)}

    +

    {React.string(post.content)}

    +
    + }) + +
    {sidebar}
    {React.array(content)} @@ -106,19 +187,84 @@ module Blog = { } let posts = [ - {id: "1", title: "Hello World", content: "Welcome to learning ReScript & React!"}, - {id: "2", title: "Installation", content: "You can install reason-react from npm."} + { + id: "1", + title: "Hello World", + content: "Welcome to learning ReScript & React!", + }, + { + id: "2", + title: "Installation", + content: "You can install reason-react from npm.", + }, ] -let blog = +let blog = +``` + +```js +function Playground$Blog(props) { + var posts = props.posts; + var sidebar = JsxRuntime.jsx("ul", { + children: posts.map(function (post) { + return JsxRuntime.jsx("li", { + children: post.title + }, post.id); + }) + }); + var content = posts.map(function (post) { + return JsxRuntime.jsxs("div", { + children: [ + JsxRuntime.jsx("h3", { + children: post.title + }), + JsxRuntime.jsx("p", { + children: post.content + }) + ] + }, post.id); + }); + return JsxRuntime.jsxs("div", { + children: [ + sidebar, + JsxRuntime.jsx("hr", {}), + content + ] + }); +} + +var Blog = { + make: Playground$Blog +}; + +var posts = [ + { + id: "1", + title: "Hello World", + content: "Welcome to learning ReScript & React!" + }, + { + id: "2", + title: "Installation", + content: "You can install reason-react from npm." + } +]; + +var blog = JsxRuntime.jsx(Playground$Blog, { + posts: posts + }); ``` + + ## Rendering `list` Values In case you ever want to render a `list` of items, you can do something like this: -```res + + +```res example type todo = {id: string, text: string} @react.component @@ -140,6 +286,33 @@ let make = () => { ``` +```js +function Playground(props) { + var items = Core__List.toArray({ + hd: { + id: "todo1", + text: "Todo 1" + }, + tl: { + hd: { + id: "todo2", + text: "Todo 2" + }, + tl: /* [] */0 + } + }).map(function (todo) { + return JsxRuntime.jsx("li", { + children: todo.text + }, todo.id); + }); + return JsxRuntime.jsx("div", { + children: items + }); +} +``` + + + We use `List.toArray` to convert our list to an array before creating our `array`. Please note that using `list` has performance impact due to extra conversion costs. 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. diff --git a/pages/docs/react/latest/beyond-jsx.mdx b/pages/docs/react/latest/beyond-jsx.mdx index add3c766b..01816cb0d 100644 --- a/pages/docs/react/latest/beyond-jsx.mdx +++ b/pages/docs/react/latest/beyond-jsx.mdx @@ -22,7 +22,7 @@ A plain React component is defined as a `('props) => React.element` function. Yo Here are some examples on how to define your own component types (often useful when interoping with existing JS code, or passing around components): -```res +```res example // Plain function type type friend = {name: string, online: bool} type friendComp = friend => React.element @@ -38,9 +38,24 @@ The types above are pretty low level (basically the JS representation of a React 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: - +
    -```res +```res prelude +module Friend = { + @react.component + let make = (~name: string, ~age: int) => {React.string(name ++ ", " ++ age->Int.toString)} +} +module Container = { + @react.component + let make = (~width: int, ~children) => {<> {React.string(width->Int.toString)} children } +} +``` + +
    + + + +```res example module Friend = { @react.component let make = (~name: string, ~children) => { @@ -51,7 +66,7 @@ module Friend = { } } ``` -```res +```res example module Friend = { type props<'name, 'children> = { name: 'name, @@ -63,6 +78,20 @@ module Friend = { } } ``` +```js +function Playground$Friend(props) { + return JsxRuntime.jsxs("div", { + children: [ + props.name, + props.children + ] + }); +} + +var Friend = { + make: Playground$Friend +}; +``` @@ -98,10 +127,9 @@ type props<'className, 'children, 'ref> = { } let make = ( - {?className, children, _}: props<'className, 'children, ReactRef.currentDomRef>, - ref: Nullable.t, -) => - make(~className, ~children, ~ref, ()) + {?className, children, _}: props<'className, 'children, ReactDOM.Ref.currentDomRef>, + ref: Nullable.t, +) => make(~className, ~children, ~ref, ()) ```
    @@ -116,10 +144,10 @@ Whenever we are using JSX with a custom component ("capitalized JSX"), we are ac -```res - +```res example +let _ = ``` -```res +```res example // classic React.createElement(Friend.make, {name: "Fred", age:20}) @@ -127,7 +155,7 @@ React.createElement(Friend.make, {name: "Fred", age:20}) React.jsx(Friend.make, {name: "Fred", age: 20}) ``` ```js -React.createElement(Playground$Friend, { name: "Fred", age: 20 }); +JsxRuntime.jsx(Playground$Friend, { name: "Fred", age: 20 }); ``` @@ -136,14 +164,14 @@ As you can see, it uses `Friend.make` to call the `React.createElement` API. In -```res +```res example {React.string("Hello")} {React.string("World")} ``` -```res +```res example // classic React.createElementVariadic( Container.make, @@ -159,7 +187,7 @@ React.jsxs( ``` ```js -React.createElement(Container, { width: 200, children: null }, "Hello", "World"); +JsxRuntime.jsx(Container, { width: 200, children: null }, "Hello", "World"); ``` @@ -173,11 +201,11 @@ Note that the `children: React.null` field has no relevance since React will onl -```res +```res example
    ``` -```res +```res example // classic ReactDOM.createDOMElementVariadic("div", ~props={title: "test"}, []) @@ -186,22 +214,22 @@ ReactDOM.jsx("div", {title: "test"}) ``` ```js -React.createElement("div", { title: "test" }); +JsxRuntime.jsx("div", { title: "test" }); ``` The same goes for uncapitalized JSX with children: - + -```res +```res example
    ``` -```res +```res example // classic ReactDOM.createDOMElementVariadic( "div", @@ -214,7 +242,16 @@ ReactDOM.jsx("div", {title: "test", children: ?ReactDOM.someElement(ReactDOM.jsx ``` ```js -React.createElement("div", { title: "test" }, React.createElement("span", undefined)); +JsxRuntime.jsx("div", { + children: JsxRuntime.jsx("span", {}), + title: "test" + }); +``` + +```js +React.createElement("div", { + title: "test" + }, React.createElement("span", undefined)); ```
    diff --git a/pages/docs/react/latest/components-and-props.mdx b/pages/docs/react/latest/components-and-props.mdx index 7d21b1d68..58ddd7750 100644 --- a/pages/docs/react/latest/components-and-props.mdx +++ b/pages/docs/react/latest/components-and-props.mdx @@ -26,7 +26,7 @@ Let's start with a first example to see how a ReScript React component looks lik -```res +```res example // src/Greeting.res @react.component let make = () => { @@ -59,7 +59,7 @@ In ReactJS, props are usually described as a single `props` record. In ReScript, -```res +```res example // src/Article.res @react.component let make = (~title: string, ~visitorCount: int, ~children: React.element) => { @@ -90,7 +90,7 @@ We can leverage the full power of labeled arguments to define optional props as -```res +```res example // Greeting.res @react.component let make = (~name: option=?) => { @@ -116,9 +116,20 @@ function Greeting(props) { In JSX, you can apply optional props with some special syntax: +
    + +```res prelude +module Greeting = { + @react.component + let make = (~name: option=?) => {React.string(name->Option.getOr(""))} +} +``` + +
    + -```res +```res example let name = Some("Andrea") @@ -162,7 +173,7 @@ as a `React.element`: -```res +```res example module MyList = { @react.component let make = (~children: React.element) => { @@ -219,7 +230,7 @@ module StringChildren = { **Component with an optional `React.element` as children:** -```res +```res example module OptionalChildren = { @react.component let make = (~children: option=?) => { @@ -268,7 +279,7 @@ The ReScript type system is really good at inferring the prop types just by look For simple cases, well-scoped usage, or experimentation, it's still fine to omit type annotations: -```res +```res example // Button.res @react.component @@ -290,7 +301,7 @@ Every ReScript component can be used in JSX. For example, if we want to use our -```res +```res example // src/App.res @react.component @@ -324,7 +335,7 @@ For example: -```res +```res example module Link = { type props = {href: string, children: React.element}; @@ -365,7 +376,7 @@ More details on the `@react.component` decorator and its generated interface can We can also represent React components as submodules, which makes it very convenient to build more complex UI without the need to create multiple files for each composite component (that's probably only used by the parent component anyways): -```res +```res example // src/Button.res module Label = { @react.component @@ -412,7 +423,7 @@ module Nested = { // will be named `File$Nested` in dev tools @react.component let make = ... -}; +} ``` If you need a dynamic name for higher-order components or you would like to set your own name you can use `React.setDisplayName(make, "NameThatShouldBeInDevTools")`. diff --git a/pages/docs/react/latest/context.mdx b/pages/docs/react/latest/context.mdx index 66eef63a4..6b3874670 100644 --- a/pages/docs/react/latest/context.mdx +++ b/pages/docs/react/latest/context.mdx @@ -25,9 +25,9 @@ Context is designed to share data that can be considered “global” for a tree -```res +```res example // src/App.res -type theme = Light | Dark; +type theme = Light | Dark module Button = { @react.component @@ -35,7 +35,7 @@ module Button = { let className = switch theme { | Light => "theme-light" | Dark => "theme-black" - }; + } } } @@ -97,7 +97,7 @@ Using context, we can avoid passing props through intermediate elements: -```res +```res example // src/App.res module ThemeContext = { diff --git a/pages/docs/react/latest/elements-and-jsx.mdx b/pages/docs/react/latest/elements-and-jsx.mdx index aca2954ff..4ac84a02e 100644 --- a/pages/docs/react/latest/elements-and-jsx.mdx +++ b/pages/docs/react/latest/elements-and-jsx.mdx @@ -18,7 +18,7 @@ Elements are the smallest building blocks of React apps. This page will explain Let's start out by creating our first React element. -```res +```res example let element =

    {React.string("Hello World")}

    ``` @@ -26,7 +26,7 @@ The binding `element` and the expression `{React.string("Hello World")}` are bot Let's say you want to create a function that handles another React element, such as `children`, you can annotate it as `React.element`: -```res +```res example let wrapChildren = (children: React.element) => {

    {React.string("Overview")}

    @@ -45,9 +45,9 @@ Fortunately our React bindings bring all necessary functionality to represent al You can compose elements into more complex structures by using JSX: -```res +```res example let greeting = React.string("Hello ") -let name = React.string("Stranger"); +let name = React.string("Stranger") // element is also of type React.element @@ -64,7 +64,7 @@ Sometimes, when doing a lot of interop with existing ReactJS codebases, you'll f Apart from using JSX to create our React elements or React components, the `React` module offers various functions to create elements from primitive data types: -```res +```res example React.string("Hello") // new element representing "Hello" React.int(1) // new element representing "1" @@ -74,7 +74,7 @@ React.float(1.0) // new element representing "1.0" It also offers `React.array` to represent multiple elements as one single element (useful for rendering a list of data, or passing children): -```res +```res example let element = React.array([ React.string("element 1"), React.string("element 2"), @@ -91,7 +91,7 @@ ReScript doesn't allow `element || null` constraints due to it's strongly typed -```res +```res example let name = Some("Andrea") let element = switch name { @@ -121,7 +121,7 @@ React.createElement("div", undefined, element); Sometimes it's necessary to pass around component functions to have more control over `React.element` creation. Use the `React.createElement` function to instantiate your elements: -```res +```res example type props = {name: string} let render = (myComp: props => React.element) => { @@ -131,7 +131,7 @@ let render = (myComp: props => React.element) => { This feature is often used when interacting with existing JS / ReactJS code. In pure ReScript React applications, you would rather pass a function that does the rendering for you (also called a "render prop"): -```res +```res example let render = (renderMyComp: (~name: string) => React.element) => {
    {renderMyComp(~name="Franz")}
    } @@ -143,7 +143,7 @@ There is also a `React.createElementVariadic` function, which takes an array of -```res +```res example type props = {title: string, children: React.element} let render = (article: props => React.element) => { @@ -183,7 +183,7 @@ This function is mostly used by our JSX transformations, so usually you want to To create DOM elements (`
    `, ``, etc.), use `ReactDOM.createDOMElementVariadic`: -```res +```res example ReactDOM.createDOMElementVariadic("div", ~props={className: "card"}, []) ``` @@ -197,11 +197,11 @@ Sometimes it's required to clone an existing element to set, overwrite or add pr -```res +```res example let original =
    // Will return a new React.element with className set to "world" -React.cloneElement(original, {"className": "world", "data-name": "some name"}); +React.cloneElement(original, {"className": "world", "data-name": "some name"}) ``` ```js var original = React.createElement("div", { diff --git a/pages/docs/react/latest/events.mdx b/pages/docs/react/latest/events.mdx index 5000cd988..de9fc930a 100644 --- a/pages/docs/react/latest/events.mdx +++ b/pages/docs/react/latest/events.mdx @@ -14,7 +14,7 @@ Depending on the event handler, the callback function will have a different type Due to the dynamic nature of JavaScript, we cannot anticipate the target type on the event. So, we need a leap of faith to grab the input value as string. -```res +```res example module App = { @react.component let make = () => { diff --git a/pages/docs/react/latest/extensions-of-props.mdx b/pages/docs/react/latest/extensions-of-props.mdx index 0419fe3f0..adda00760 100644 --- a/pages/docs/react/latest/extensions-of-props.mdx +++ b/pages/docs/react/latest/extensions-of-props.mdx @@ -12,16 +12,27 @@ canonical: "/docs/react/latest/spread-props" JSX props spread is supported now, but in a stricter way than in JS. +
    + +```res prelude +module Comp = { + type props = {a?: string, b?: string} + external make: React.component = "default" +} +``` + +
    + -```res +```res example +let props = {Comp.b: "b"} ``` ```js -React.createElement(Comp, { - a: "a", - b: "b" -}); +var props = {b: "b"}; +var newrecord = Caml_obj.obj_dup(props); +return JsxRuntime.jsx(default, (newrecord.a = "a", newrecord)); ``` @@ -52,17 +63,17 @@ You can control the definition of the `props` type by passing as argument to `@r -```res -type sharedprops<'x, 'y> = {x: 'x, y: 'y, z:string} +```res example +type sharedProps<'x, 'y> = {x: 'x, y: 'y, z:string} module C1 = { @react.component(:sharedProps<'a, 'b>) - let make = (~x, ~y) => React.string(x ++ y ++ z) + let make = (~x, ~y, ~z) => React.string(x ++ y ++ z) } module C2 = { @react.component(:sharedProps) - let make = (~x, ~y) => React.string(x ++ y ++ z) + let make = (~x, ~y, ~z) => React.string(x ++ y ++ z) } module C3 = { @@ -72,17 +83,17 @@ module C3 = { } ``` -```res -type sharedprops<'x, 'y> = {x: 'x, y: 'y, z: string} +```res example +type sharedProps<'x, 'y> = {x: 'x, y: 'y, z: string} module C1 = { type props<'a, 'b> = sharedProps<'a, 'b> - let make = ({x, y, _}: props<_>) => React.string(x ++ y ++ z) + let make = ({x, y, z}: props<_>) => React.string(x ++ y ++ z) } module C2 = { type props<'b> = sharedProps - let make = ({x, y, _}: props<_>) => React.string(x ++ y ++ z) + let make = ({x, y, z}: props<_>) => React.string(x ++ y ++ z) } module C3 = { diff --git a/pages/docs/react/latest/forwarding-refs.mdx b/pages/docs/react/latest/forwarding-refs.mdx index 76431c114..617198278 100644 --- a/pages/docs/react/latest/forwarding-refs.mdx +++ b/pages/docs/react/latest/forwarding-refs.mdx @@ -16,7 +16,7 @@ Ref forwarding is a technique for automatically passing a [React.ref](./refs-and Consider a FancyButton component that renders the native button DOM element: -```res +```res example // FancyButton.res @react.component @@ -43,7 +43,7 @@ A `React.ref` can be passed down like any other prop. The component will take ca In the example below, `FancyInput` defines a prop `inputRef` that will be forwarded to its `input` element: -```res +```res example // App.res module FancyInput = { @@ -84,7 +84,7 @@ This is how the previous example would look like with the `React.forwardRef` app -```res +```res example // App.res module FancyInput = { diff --git a/pages/docs/react/latest/hooks-context.mdx b/pages/docs/react/latest/hooks-context.mdx index 8af68ddfe..df3cdd133 100644 --- a/pages/docs/react/latest/hooks-context.mdx +++ b/pages/docs/react/latest/hooks-context.mdx @@ -30,7 +30,7 @@ Accepts a `React.Context.t` (the value returned from `React.createContext`) and -```res +```res example // App.res module ThemeContext = { let context = React.createContext("light") diff --git a/pages/docs/react/latest/hooks-custom.mdx b/pages/docs/react/latest/hooks-custom.mdx index 42a1eac02..1176591b0 100644 --- a/pages/docs/react/latest/hooks-custom.mdx +++ b/pages/docs/react/latest/hooks-custom.mdx @@ -20,17 +20,17 @@ Let's go back to a previous example from our [React.useEffect section](./hooks-e -```res {16-31} +```res example {16-31} // FriendStatus.res module ChatAPI = { // Imaginary globally available ChatAPI for demo purposes - type status = { isOnline: bool }; - @val external subscribeToFriendStatus: (string, status => unit) => unit = "subscribeToFriendStatus"; - @val external unsubscribeFromFriendStatus: (string, status => unit) => unit = "unsubscribeFromFriendStatus"; + type status = { isOnline: bool } + @val external subscribeToFriendStatus: (string, status => unit) => unit = "subscribeToFriendStatus" + @val external unsubscribeFromFriendStatus: (string, status => unit) => unit = "unsubscribeFromFriendStatus" } -type state = Offline | Loading | Online; +type state = Offline | Loading | Online @react.component let make = (~friendId: string) => { @@ -43,15 +43,15 @@ let make = (~friendId: string) => { }) } - ChatAPI.subscribeToFriendStatus(friendId, handleStatusChange); - setState(_ => Loading); + ChatAPI.subscribeToFriendStatus(friendId, handleStatusChange) + setState(_ => Loading) let cleanup = () => { ChatAPI.unsubscribeFromFriendStatus(friendId, handleStatusChange) } Some(cleanup) - }) + }, [friendId]) let text = switch(state) { | Offline => friendId ++ " is offline" @@ -117,14 +117,14 @@ Now let’s say that our chat application also has a contact list, and we want t ```res {15-30} // FriendListItem.res -type state = Offline | Loading | Online; +type state = Offline | Loading | Online // module ChatAPI = {...} type friend = { id: string, name: string -}; +} @react.component let make = (~friend: friend) => { @@ -137,8 +137,8 @@ let make = (~friend: friend) => { }) } - ChatAPI.subscribeToFriendStatus(friend.id, handleStatusChange); - setState(_ => Loading); + ChatAPI.subscribeToFriendStatus(friend.id, handleStatusChange) + setState(_ => Loading) let cleanup = () => { ChatAPI.unsubscribeFromFriendStatus(friend.id, handleStatusChange) @@ -292,7 +292,7 @@ The purpose of our `useFriendStatus` Hook is to subscribe us to a friend’s sta ```res let useFriendStatus = (friendId: string): status { - let (state, setState) = React.useState(_ => Offline); + let (state, setState) = React.useState(_ => Offline) // ... @@ -315,11 +315,11 @@ Now that we’ve extracted this logic to a useFriendStatus hook, we can just use ```res {6} // FriendStatus.res -type friend = { id: string }; +type friend = { id: string } @react.component let make = (~friend: friend) => { - let onlineState = FriendStatusHook.useFriendStatus(friend.id); + let onlineState = FriendStatusHook.useFriendStatus(friend.id) let status = switch(onlineState) { | FriendStatusHook.Online => "Online" @@ -327,7 +327,7 @@ let make = (~friend: friend) => { | Offline => "Offline" } - React.string(status); + React.string(status) } ``` ```js @@ -363,7 +363,7 @@ function FriendStatus(Props) { // FriendListItem.res @react.component let make = (~friend: friend) => { - let onlineState = FriendStatusHook.useFriendStatus(friend.id); + let onlineState = FriendStatusHook.useFriendStatus(friend.id) let color = switch(onlineState) { | Offline => "red" diff --git a/pages/docs/react/latest/hooks-effect.mdx b/pages/docs/react/latest/hooks-effect.mdx index 0899df691..404c26cae 100644 --- a/pages/docs/react/latest/hooks-effect.mdx +++ b/pages/docs/react/latest/hooks-effect.mdx @@ -23,39 +23,38 @@ There are two common kinds of side effects in React components: those that don -```res +```res example // Runs after every completed render -React.useEffect(() => { +React.useEffectOnEveryRender(() => { // Run effects None // or Some(() => {}) }) // Runs only once right after mounting the component -React.useEffect0(() => { +React.useEffect(() => { // Run effects None // or Some(() => {}) -}) +}, []) + +// Dummy props +let prop1 = 1 and prop2 = 2 and prop3 = 3 // Runs everytime `prop1` has changed -React.useEffect1(() => { +React.useEffect(() => { // Run effects based on prop1 None }, [prop1]) // Runs everytime `prop1` or `prop2` has changed -React.useEffect2(() => { +React.useEffect(() => { // Run effects based on prop1 / prop2 None }, (prop1, prop2)) -React.useEffect3(() => { +React.useEffect(() => { None -}, (prop1, prop2, prop3)); - -// useEffect4...7 with according dependency -// tuple just like useEffect3 - +}, (prop1, prop2, prop3)) ``` ```js @@ -70,11 +69,11 @@ React.useEffect((function () { }), [ prop1, prop2, prop3 ]); `React.useEffect` receives a function that contains imperative, possibly effectful code, and returns a value `option unit>` as a potential cleanup function. -A `useEffect` call may receive an additional array of dependencies (see `React.useEffect1` / `React.useEffect2...7`). The effect function will run whenever one of the provided dependencies has changed. More details on why this is useful [down below](#effect-dependencies). +A `useEffect` call may receive an additional array of dependencies. The effect function will run whenever one of the provided dependencies has changed. More details on why this is useful [down below](#effect-dependencies). -**Note:** You probably wonder why `React.useEffect1` receives an `array`, and `useEffect2` etc require a `tuple` (e.g. `(prop1, prop2)`) for the dependency list. That's because a tuple can receive multiple values of different types, whereas an `array` only accepts values of identical types. It's possible to replicate `useEffect2` by doing `React.useEffect1(fn, [1, 2])`, on other hand the type checker wouldn't allow `React.useEffect1(fn, [1, "two"])`. +**Note:** You probably wonder why the `React.useEffect` with only one dependency receives an `array`, while `useEffect`'s with multiple dependencies require a `tuple` (e.g. `(prop1, prop2)`) for the dependency list. That's because a tuple can receive multiple values of different types, whereas an `array` only accepts values of identical types. It's possible to replicate a `useEffect` with multiple dependencies by doing `React.useEffect(fn, [1, 2])`, on other hand the type checker wouldn't allow `React.useEffect(fn, [1, "two"])`. -`React.useEffect` will run its function after every completed render, while `React.useEffect0` will only run the effect on the first render (when the component has mounted). +`React.useEffectOnEveryRender` will run its function after every completed render, while `React.useEffect` will only run the effect on the first render (when the component has mounted). ## Examples @@ -85,29 +84,32 @@ Sometimes, we want to run some additional code after React has updated the DOM. As an example, let's write a counter component that updates `document.title` on every render: - - -```res +```res prelude // Counter.res module Document = { - type t; - @val external document: t = "document"; + type t + @val external document: t = "document" @set external setTitle: (t, string) => unit = "title" } +``` + + + +```res example @react.component let make = () => { - let (count, setCount) = React.useState(_ => 0); + let (count, setCount) = React.useState(_ => 0) - React.useEffect(() => { - open Document + React.useEffectOnEveryRender(() => { + open! Document document->setTitle(`You clicked ${Belt.Int.toString(count)} times!`) None - }, ); + }) let onClick = (_evt) => { setCount(prev => prev + 1) - }; + } let msg = "You clicked" ++ Belt.Int.toString(count) ++ "times" @@ -147,11 +149,11 @@ function Counter(Props) { In case we want to make the effects dependent on `count`, we can just use following `useEffect` call instead: ```res - React.useEffect1(() => { - open Document + React.useEffect(() => { + open! Document document->setTitle(`You clicked ${Belt.Int.toString(count)} times!`) None - }, [count]); + }, [count]) ``` Now instead of running an effect on every render, it will only run when `count` has a different value than in the render before. @@ -162,33 +164,34 @@ Earlier, we looked at how to express side effects that don’t require any clean Let's look at an example that gracefully subscribes, and later on unsubscribes from some subscription API: - - -```res -// FriendStatus.res - +```res prelude module ChatAPI = { // Imaginary globally available ChatAPI for demo purposes - type status = { isOnline: bool }; - @val external subscribeToFriendStatus: (string, status => unit) => unit = "subscribeToFriendStatus"; - @val external unsubscribeFromFriendStatus: (string, status => unit) => unit = "unsubscribeFromFriendStatus"; + type status = { isOnline: bool } + @val external subscribeToFriendStatus: (string, status => unit) => unit = "subscribeToFriendStatus" + @val external unsubscribeFromFriendStatus: (string, status => unit) => unit = "unsubscribeFromFriendStatus" } -type state = Offline | Loading | Online; +type state = Offline | Loading | Online +``` + + +```res example +// FriendStatus.res @react.component let make = (~friendId: string) => { let (state, setState) = React.useState(_ => Offline) - React.useEffect(() => { + React.useEffectOnEveryRender(() => { let handleStatusChange = (status) => { setState(_ => { status.ChatAPI.isOnline ? Online : Offline }) } - ChatAPI.subscribeToFriendStatus(friendId, handleStatusChange); - setState(_ => Loading); + ChatAPI.subscribeToFriendStatus(friendId, handleStatusChange) + setState(_ => Loading) let cleanup = () => { ChatAPI.unsubscribeFromFriendStatus(friendId, handleStatusChange) @@ -260,41 +263,45 @@ In some cases, cleaning up or applying the effect after every render might creat ```res // from a previous example above -React.useEffect1(() => { - open Document +React.useEffect(() => { + open! Document document->setTitle(`You clicked ${Belt.Int.toString(count)} times!`) - None; -}, [count]); + None +}, [count]) ``` -Here, we pass `[count]` to `useEffect1` as a dependency. What does this mean? If the `count` is 5, and then our component re-renders with count still equal to 5, React will compare `[5]` from the previous render and `[5]` from the next render. Because all items within the array are the same (5 === 5), React would skip the effect. That’s our optimization. +Here, we pass `[count]` to `useEffect` as a dependency. What does this mean? If the `count` is 5, and then our component re-renders with count still equal to 5, React will compare `[5]` from the previous render and `[5]` from the next render. Because all items within the array are the same (5 === 5), React would skip the effect. That’s our optimization. When we render with count updated to 6, React will compare the items in the `[5]` array from the previous render to items in the `[6]` array from the next render. This time, React will re-apply the effect because `5 !== 6`. If there are multiple items in the array, React will re-run the effect even if just one of them is different. This also works for effects that have a cleanup phase: -```res -// from a previous example above -React.useEffect1(() => { - let handleStatusChange = (status) => { - setState(_ => { - status.ChatAPI.isOnline ? Online : Offline - }) - } +```res example +@react.component +let make = (~friendId: string) => { + let (state, setState) = React.useState(_ => Offline) + // from a previous example above + React.useEffect(() => { + let handleStatusChange = (status) => { + setState(_ => { + status.ChatAPI.isOnline ? Online : Offline + }) + } - ChatAPI.subscribeToFriendStatus(friendId, handleStatusChange); - setState(_ => Loading); + ChatAPI.subscribeToFriendStatus(friendId, handleStatusChange) + setState(_ => Loading) - let cleanup = () => { - ChatAPI.unsubscribeFromFriendStatus(friendId, handleStatusChange) - } + let cleanup = () => { + ChatAPI.unsubscribeFromFriendStatus(friendId, handleStatusChange) + } - Some(cleanup) -}, [friendId]) // Only re-subscribe if friendId changes + Some(cleanup) + }, [friendId]) // Only re-subscribe if friendId changes +} ``` **Important:** If you use this optimization, make sure the array includes all values from the component scope (such as props and state) that change over time and that are used by the effect. Otherwise, your code will reference stale values from previous renders -If you want to run an effect and clean it up only once (on mount and unmount), use `React.useEffect0`. +If you want to run an effect and clean it up only once (on mount and unmount), use `React.useEffect` with an empty dependency array `[]`. If you are interested in more performance optmization related topics, have a look at the ReactJS [Performance Optimization Docs](https://reactjs.org/docs/hooks-faq.html#performance-optimizations) for more detailed infos. diff --git a/pages/docs/react/latest/hooks-overview.mdx b/pages/docs/react/latest/hooks-overview.mdx index 1b05ca23a..1e4fc52be 100644 --- a/pages/docs/react/latest/hooks-overview.mdx +++ b/pages/docs/react/latest/hooks-overview.mdx @@ -26,15 +26,15 @@ Just for a quick look, here is an example of a `Counter` component that allows a -```res +```res example // Counter.res @react.component let make = () => { - let (count, setCount) = React.useState(_ => 0); + let (count, setCount) = React.useState(_ => 0) let onClick = (_evt) => { setCount(prev => prev + 1) - }; + } let msg = "You clicked" ++ Belt.Int.toString(count) ++ "times" diff --git a/pages/docs/react/latest/hooks-reducer.mdx b/pages/docs/react/latest/hooks-reducer.mdx index 8b36747ca..2584e6351 100644 --- a/pages/docs/react/latest/hooks-reducer.mdx +++ b/pages/docs/react/latest/hooks-reducer.mdx @@ -38,7 +38,7 @@ An alternative to [useState](./hooks-state). Accepts a reducer of type `(state, -```res +```res example // Counter.res type action = Inc | Dec @@ -103,7 +103,7 @@ You can leverage the full power of variants to express actions with data payload -```res +```res example // TodoApp.res type todo = { @@ -278,7 +278,7 @@ It lets you extract the logic for calculating the initial state outside the redu -```res +```res example // Counter.res type action = Inc | Dec | Reset(int) diff --git a/pages/docs/react/latest/hooks-ref.mdx b/pages/docs/react/latest/hooks-ref.mdx index 053d632df..b6afb2dd5 100644 --- a/pages/docs/react/latest/hooks-ref.mdx +++ b/pages/docs/react/latest/hooks-ref.mdx @@ -17,7 +17,7 @@ The `useRef` hooks creates and manages mutable containers inside your React comp ```res -let refContainer = React.useRef(initialValue); +let refContainer = React.useRef(initialValue) ``` ```js @@ -49,7 +49,7 @@ More infos on direct DOM manipulation can be found in the [Refs and the DOM](./r -```res +```res example // TextInputWithFocusButton.res @send external focus: Dom.element => unit = "focus" @@ -94,7 +94,7 @@ Reusing the example from our [Refs and the DOM](./refs-and-the-dom#callback-refs -```res +```res example // CustomTextInput.res @send external focus: Dom.element => unit = "focus" diff --git a/pages/docs/react/latest/hooks-state.mdx b/pages/docs/react/latest/hooks-state.mdx index a3a9793c9..7099fe408 100644 --- a/pages/docs/react/latest/hooks-state.mdx +++ b/pages/docs/react/latest/hooks-state.mdx @@ -41,7 +41,7 @@ The `setState` function can be passed down to other components as well, which is ### Using State for a Text Input -```res +```res example @react.component let make = () => { let (text, setText) = React.useState(_ => ""); @@ -65,7 +65,7 @@ In this example, we are creating a `ThemeContainer` component that manages a `da -```res +```res example // ThemeContainer.res module ControlPanel = { @react.component @@ -131,32 +131,4 @@ function ThemeContainer(Props) { -Note that whenever `setDarkmode` is returning a new value (e.g. switching from `true` -> `false`), it will cause a re-render for `ThemeContainer`'s `className` and the toggle text of its nested `ControlPanel`. - - -## Uncurried Version - -For cleaner JS output, you can use `React.Uncurried.useState` instead: - - - -```res -let (state, setState) = React.Uncurried.useState(_ => 0) - -setState(. prev => prev + 1) -``` - -```js -var match = React.useState(function () { - return 0; - }); - -var setState = match[1]; - -setState(function (prev) { - return prev + 1 | 0; - }); -``` - - - +Note that whenever `setDarkmode` is returning a new value (e.g. switching from `true` -> `false`), it will cause a re-render for `ThemeContainer`'s `className` and the toggle text of its nested `ControlPanel`. \ No newline at end of file diff --git a/pages/docs/react/latest/import-export-reactjs.mdx b/pages/docs/react/latest/import-export-reactjs.mdx index dc4717af7..52d47464c 100644 --- a/pages/docs/react/latest/import-export-reactjs.mdx +++ b/pages/docs/react/latest/import-export-reactjs.mdx @@ -16,7 +16,7 @@ To reuse a React component in ReScript, create a new module, specify the compone -```res +```res example module Confetti = { @module("react-confetti") @react.component external make: (~width: int, ~height: int) => React.element = "default" @@ -54,7 +54,7 @@ Use "default" to indicate the default export, or specify a named export if neede -```res +```res example // Equivalent of import { Foo } from "bar" module Foo = { @module("bar") @react.component @@ -72,7 +72,7 @@ You can define a separate type for your component's props within the module. -```res +```res example module Confetti = { type confettiProps = { width: int, @@ -111,7 +111,7 @@ To define optional props, use the `?` symbol. -```res +```res example module Confetti = { type confettiProps = { width: int, @@ -152,7 +152,7 @@ To accept existing DOM props for a component, extend the `JsxDOM.domProps` type. -```res +```res example module Foo = { type fooProps = { ...JsxDOM.domProps, diff --git a/pages/docs/react/latest/lazy-components.mdx b/pages/docs/react/latest/lazy-components.mdx index 87c6a1c00..519464dfb 100644 --- a/pages/docs/react/latest/lazy-components.mdx +++ b/pages/docs/react/latest/lazy-components.mdx @@ -20,7 +20,7 @@ ReScript comes with APIs for doing dynamic imports both for single values and fo Let's look at a small example. First we'll define a simple component: -```rescript +```res example // Title.res @react.component let make = (~text) => { diff --git a/pages/docs/react/latest/memo.mdx b/pages/docs/react/latest/memo.mdx index 34b05a8c4..4e6d6c119 100644 --- a/pages/docs/react/latest/memo.mdx +++ b/pages/docs/react/latest/memo.mdx @@ -15,7 +15,7 @@ This memoized version of your component will usually not be re-rendered when its -```res +```res example @react.component let make = React.memo((~a: int, ~b: string) => {
    @@ -54,7 +54,7 @@ To work around this, you can redefine the make binding: -```res +```res example @react.component let make = (~disabled, ~onClick) => {