|
| 1 | +--- |
| 2 | +title: Components and Props |
| 3 | +description: "Basic concepts for components and props in ReasonReact" |
| 4 | +canonical: "/docs/react/latest/components-and-props" |
| 5 | +category: "Main Concepts" |
| 6 | +--- |
| 7 | + |
| 8 | +# Components and Props |
| 9 | + |
| 10 | +<Intro> |
| 11 | + |
| 12 | +Components let you split the UI into independent, reusable pieces, and think about each piece in isolation. This page provides an introduction to the idea of components. |
| 13 | + |
| 14 | +</Intro> |
| 15 | + |
| 16 | +## What is a Component? |
| 17 | + |
| 18 | +A React component is a function describing a UI element that receives a `props` object as a parameter (data describing the dynamic parts of the UI) and returns a `React.element`. |
| 19 | + |
| 20 | +The nice thing about this concept is that you can solely focus on the input and output. The component function receives some data and returns some opaque `React.element` that is managed by the React framework to render your UI. |
| 21 | + |
| 22 | +> If you want to know more about the low level details on how a component interface is implemented, refer to the advanced topic [React JSX Transformation](./react-jsx-transformation) |
| 23 | +
|
| 24 | +## Component Example |
| 25 | + |
| 26 | +Let's start with a first example to see how a ReScript React component looks like: |
| 27 | + |
| 28 | +<CodeTab labels={["ReScript", "JS Output"]}> |
| 29 | + |
| 30 | +```res |
| 31 | +// src/Greeting.res |
| 32 | +@react.component |
| 33 | +let make = () => { |
| 34 | + <div> |
| 35 | + {React.string("Hello ReScripters!")} |
| 36 | + </div> |
| 37 | +} |
| 38 | +``` |
| 39 | +```js |
| 40 | +var React = require("react"); |
| 41 | + |
| 42 | +function Greeting(Props) { |
| 43 | + return React.createElement("div", undefined, "Hello ReScripters!"); |
| 44 | +} |
| 45 | + |
| 46 | +var make = Greeting; |
| 47 | +``` |
| 48 | + |
| 49 | +</CodeTab> |
| 50 | + |
| 51 | +**Important:** Always make sure to name your component function `make` and don't forget to add the `@react.component` attribute. |
| 52 | + |
| 53 | +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 `<div> Hello ReScripters! </div>` in the rendered DOM. |
| 54 | + |
| 55 | +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 `<div>` transforms into a `React.createElement("div",...)` call in JavaScript. |
| 56 | + |
| 57 | +## Defining Props |
| 58 | + |
| 59 | +In ReactJS, props are usually described as a single `props` object. In ReScript, we use [labeled arguments](docs/manual/latest/function#labeled-arguments) to define our props parameters instead. Here's an example: |
| 60 | + |
| 61 | +<CodeTab labels={["ReScript", "JS Output"]}> |
| 62 | + |
| 63 | +```res |
| 64 | +// src/Article.res |
| 65 | +@react.component |
| 66 | +let make = (~title: string, ~visitorCount: int, ~children: React.element) => { |
| 67 | + let visitorCountMsg = "You are visitor number: " ++ Belt.Int.toString(visitorCount); |
| 68 | + <div> |
| 69 | + <div> {React.string(title)} </div> |
| 70 | + <div> {React.string(vistorCount)} </div> |
| 71 | + children |
| 72 | + </div> |
| 73 | +} |
| 74 | +``` |
| 75 | +```js |
| 76 | +var React = require("react"); |
| 77 | + |
| 78 | +function Article(Props) { |
| 79 | + var title = Props.title; |
| 80 | + var visitorCount = Props.visitorCount; |
| 81 | + var children = Props.children; |
| 82 | + var visitorCountMsg = "You are visitor number: " + String(visitorCount); |
| 83 | + return React.createElement("div", undefined, React.createElement("div", undefined, title), React.createElement("div", undefined, visitorCountMsg), children); |
| 84 | +} |
| 85 | + |
| 86 | +var make = Article; |
| 87 | +``` |
| 88 | + |
| 89 | +</CodeTab> |
| 90 | + |
| 91 | +### Optional Props |
| 92 | + |
| 93 | +### Type Inference |
| 94 | + |
| 95 | +The ReScript type system excels at computing the actual prop types through usage. We recommend to explicitly type your props (especially for exported components) to help your coworkers understand the types. For simple cases or experimentation, it's still fine to omit them: |
| 96 | + |
| 97 | + |
| 98 | +``` |
| 99 | +// Button.res |
| 100 | +
|
| 101 | +@react.component |
| 102 | +let make = (~onClick, ~msg, ~children) => { |
| 103 | + <div onClick> |
| 104 | + {React.string(msg)} |
| 105 | + children |
| 106 | + </div> |
| 107 | +} |
| 108 | +``` |
| 109 | + |
| 110 | +In the example above, `onClick` will be inferred as `ReactEvent.Mouse.t => unit`, `msg` as `string` and `children` as `React.element`. Type inference is very useful to ommit a lot of annoying type annotations, especially for private functions that pass around values to other functions. |
| 111 | + |
| 112 | + |
| 113 | +## Using Components in JSX |
| 114 | + |
| 115 | +Every ReScript component can be used in JSX. For example, if we want to use our `Greeting` component within our `App` component, we can do this: |
| 116 | + |
| 117 | +<CodeTab labels={["ReScript", "JS Output"]}> |
| 118 | + |
| 119 | +```res |
| 120 | +// src/App.re |
| 121 | +
|
| 122 | +@react.component |
| 123 | +let make = () => { |
| 124 | + <div> |
| 125 | + <Greeting/> |
| 126 | + </div> |
| 127 | +} |
| 128 | +``` |
| 129 | +```js |
| 130 | +var React = require("react"); |
| 131 | +var Greeting = require("./Greeting.js") |
| 132 | + |
| 133 | +function App(Props) { |
| 134 | + return React.createElement("div", undefined, React.createElement(Greeting.make, {})); |
| 135 | +} |
| 136 | + |
| 137 | +var make = App; |
| 138 | +``` |
| 139 | + |
| 140 | +</CodeTab> |
| 141 | + |
| 142 | +**Note:** React components are capitalized; primitive DOM elements like `div` or `button` are uncapitalized. More infos on the JSX specifics and code transformations can be found in our [JSX section](/docs/manual/latest/jsx#capitalized-tag) in our language manual. |
| 143 | + |
| 144 | + |
| 145 | +## Submodule Components |
| 146 | + |
| 147 | +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): |
| 148 | + |
| 149 | +```res |
| 150 | +// src/Button.res |
| 151 | +module Label = { |
| 152 | + @react.component |
| 153 | + let make = (~title: string) => { |
| 154 | + <div className="myLabel"> {React.string(title)} </div> |
| 155 | + } |
| 156 | +} |
| 157 | +
|
| 158 | +@react.component |
| 159 | +let make = (~children) => { |
| 160 | + <div> |
| 161 | + <Label title="Getting Started" /> |
| 162 | + children |
| 163 | + </div> |
| 164 | +} |
| 165 | +``` |
| 166 | + |
| 167 | +The `Button.res` file defined in above is now containing a `Label` component, that can also be used by other components, either by writing the fully qualified module name (`<Button.Label title="My Button"/>`) or by using a module alias to shortcut the full qualifier: |
| 168 | + |
| 169 | + |
| 170 | +```res |
| 171 | +module Label = Button.Label |
| 172 | +
|
| 173 | +let content = <Label title="Test"/> |
| 174 | +``` |
| 175 | + |
| 176 | + |
| 177 | +## Tips & Tricks |
| 178 | + |
| 179 | +- Start with one component file and utilize submodule components as your component grows. Consider splitting up in multiple files when really necessary. |
| 180 | +- Keep your directory hierarchy flat. Instead of `article/Header.res` use `ArticleHeader.res` etc. Filenames are unique across the codebase, so filenames tend to be very specific `ArticleUserHeaderCard.res`, which is not necessarily a bad thing, since it clearly expresses the intent of the component within its name, and makes it also very easy to find, match and refactor across the whole codebase. |
0 commit comments