From eda1f43cb8d56fc1e296cbee9f0f2223d1085ee5 Mon Sep 17 00:00:00 2001 From: nojaf Date: Tue, 25 Mar 2025 15:58:57 +0100 Subject: [PATCH] Add docs for @react.componentWithProps --- .../decorator_react_component_with_props.mdx | 51 +++++++++++++ .../react/latest/components-and-props.mdx | 71 ++++++++++++++++++- 2 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 misc_docs/syntax/decorator_react_component_with_props.mdx diff --git a/misc_docs/syntax/decorator_react_component_with_props.mdx b/misc_docs/syntax/decorator_react_component_with_props.mdx new file mode 100644 index 000000000..689b32e76 --- /dev/null +++ b/misc_docs/syntax/decorator_react_component_with_props.mdx @@ -0,0 +1,51 @@ +--- +id: "react-component-with-props-decorator" +keywords: ["react", "component", "props", "decorator"] +name: "@react.componentWithProps" +summary: "This is the `@react.componentWithProps` decorator." +category: "decorators" +--- + +The `@react.componentWithProps` decorator is used to annotate functions that are [React](/docs/react/latest/elements-and-jsx) components which take in a record type as prop. +This decorator will ensure your component gets an uppercased name. + + +> **Note** +> The `@react.componentWithProps` decorator requires the [react-jsx or jsx config](/docs/react/latest/installation) to be set in your `rescript.json` to enable the required React transformations. + +### Example + + + +```res +module Hey = { + type props = { + name: string, + } + + @react.componentWithProps + let make = (props: props) => { + + } +} +``` + +```js +import * as JsxRuntime from "react/jsx-runtime"; + +function Playground$Hey(props) { + return JsxRuntime.jsx("button", { + children: "Hello " + props.name + "!" + }); +} + +let Hey = { + make: Playground$Hey +}; +``` + + + +### References + +* [React Components](/docs/react/latest/components-and-props) diff --git a/pages/docs/react/latest/components-and-props.mdx b/pages/docs/react/latest/components-and-props.mdx index 58ddd7750..fc3ab0d87 100644 --- a/pages/docs/react/latest/components-and-props.mdx +++ b/pages/docs/react/latest/components-and-props.mdx @@ -39,7 +39,9 @@ let make = () => { import * as React from "react"; function Greeting(props) { - return React.createElement("div", undefined, "Hello ReScripters!"); + return JsxRuntime.jsx("div", { + children: "Hello ReScripters!" + }); } var make = Greeting; @@ -51,7 +53,7 @@ var make = Greeting; We've created a `Greeting.res` file that contains a `make` function that doesn't receive any props (the function doesn't receive any parameters), and returns a `React.element` that represents `
Hello ReScripters!
` in the rendered DOM. -You can also see in the the JS output that the function we created was directly translated into the pure JS version of a ReactJS component. Note how a `
` transforms into a `React.createElement("div",...)` call in JavaScript. +You can also see in the the JS output that the function we created was directly translated into the pure JS version of a ReactJS component. Note how a `
` transforms into a `JsxRuntime.jsx("div",...)` call in JavaScript. ## Defining Props @@ -272,6 +274,69 @@ The best way to approach this kind of issue is by using props instead of childre **The best use-case for `children` is to pass down `React.element`s without any semantic order or implementation details!** +## @react decorators + +You might've wondered what `@react.component` actually does. +It's a decorator that tells the ReScript compiler to treat the function as a React component, transforming it at the syntax level. + +In JavaScript, a React component is just a function that takes props (an object) and returns JSX. In ReScript, props are typically represented as a record type. +The `@react.component` decorator automatically generates that record type and wraps the function for you—so you don't have to. + +```res +// Counter.res + +@react.component +let make = (~title, ~count) => { +

{React.string(title)} {React.int(count)}

+} + +// This is equivalent to writing: + +type props = {title: string, count: int} + +let \"Counter" = ({title, count}: props) => { +

{React.string(title)} {React.int(count)}

+} +``` + +However, writing it manually like this means you lose the `make` function name, which prevents JSX from working as expected when using the component elsewhere. + +Having an uppercased function name also helps distinguish React components from regular functions in [React DevTools](https://react.dev/learn/react-developer-tools). + +If you prefer defining your own props record, you can use `@react.componentWithProps`. This gives you full control over the props type while still generating a proper uppercased component. + + + +```res +// Counter.res +type props = {title: string, count: int} + +@react.componentWithProps +let make = (props: props) => { +

+ {React.string(props.title)} + {React.int(props.count)} +

+} +``` + +```js +import * as JsxRuntime from "react/jsx-runtime"; + +function Counter(props) { + return JsxRuntime.jsxs("h1", { + children: [ + props.title, + props.count + ] + }); +} + +let make = Counter; +``` + +
+ ## Props & Type Inference The ReScript type system is really good at inferring the prop types just by looking at its prop usage. @@ -405,7 +470,7 @@ let content =