Skip to content

New react docs (only content) #96

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions data/sidebar_react_latest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"Overview": [
"introduction",
"installation"
],
"Main Concepts": [
"elements-and-jsx",
"rendering-elements",
"components-and-props",
"arrays-and-keys",
"refs-and-the-dom",
"context"
],
"Hooks & State Management": [
"hooks-overview",
"hooks-effect",
"hooks-state",
"hooks-reducer",
"hooks-context",
"hooks-ref",
"hooks-custom"
],
"Guides": [
"beyond-jsx",
"forwarding-refs"
]
}
2 changes: 1 addition & 1 deletion pages/docs/manual/latest/api/js.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ or the JavaScript
[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
classes.

It is meant as a zero-abstraction interop layer and directly exposes JavaScript functions as they are. If you can find your API in this module, prefer this over an equivalent Belt helper. For example, prefer [Js.Array2](js/array2) over [Belt.Array](belt/array)
It is meant as a zero-abstraction interop layer and directly exposes JavaScript functions as they are. If you can find your API in this module, prefer this over an equivalent Belt helper. For example, prefer [Js.Array2](js/array-2) over [Belt.Array](belt/array)

## Argument Order

Expand Down
2 changes: 1 addition & 1 deletion pages/docs/manual/v8.0.0/api/js.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ or the JavaScript
[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
classes.

It is meant as a zero-abstraction interop layer and directly exposes JavaScript functions as they are. If you can find your API in this module, prefer this over an equivalent Belt helper. For example, prefer [Js.Array2](js/array2) over [Belt.Array](belt/array)
It is meant as a zero-abstraction interop layer and directly exposes JavaScript functions as they are. If you can find your API in this module, prefer this over an equivalent Belt helper. For example, prefer [Js.Array2](js/array-2) over [Belt.Array](belt/array)

## Argument Order

Expand Down
124 changes: 124 additions & 0 deletions pages/docs/react/latest/arrays-and-keys.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
---
title: Arrays and Keys
description: "Rendering arrays and handling keys in ReScript and React"
canonical: "/docs/react/latest/arrays-and-keys"
---

# Arrays and Keys

<Intro>

Whenever we are transforming data into an array of elements and put it in our React tree, we need to make sure to give every element an unique identifier to help React distinguish elements for each render. This page will explain the `key` attribute and how to apply it whenever we need to map data to `React.element`s.

</Intro>

## Keys & Rendering Arrays

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];

let items = Belt.Array.map(numbers, (number) => {
<li key={Belt.Int.toString(number)}> {React.int(number)} </li>
})
```

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
type todo = {id: string, text: string}

let todos = [
{id: "todo1", text: "Todo 1"},
{id: "todo2", text: "Todo 2"}
]

let items = Belt.Array.map(todos, todo => {
<li key={todo.id}> {React.string(todo.text)} </li>
})
```

If you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort:

```res {2,3}
let items = Belt.Array.mapWithIndex(todos, (todo, i) => {
// Only do this if items have no stable id
<li key={i}>
{todo.text}
</li>
});
```

### 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}
type post = {id: string, title: string, content: string}

module Blog = {
@react.component
let make = (~posts: array<post>) => {
let sidebar =
<ul>
{
Belt.Array.map(posts, (post) => {
<li key={post.id}>
{React.string(post.title)}
</li>
})->React.array
}
</ul>

let content = Belt.Array.map(posts, (post) => {
<div key={post.id}>
<h3>{React.string(post.title)}</h3>
<p>{React.string(post.content)}</p>
</div>
});

<div>
{sidebar}
<hr />
{React.array(content)}
</div>
}
}

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."}
]

let blog = <Blog posts/>
```


## Rendering `list` Values

In case you ever want to render a `list` of items, you can do something like this:

```res
type todo = {id: string, text: string}
let todoList = list{
{id: "todo1", text: "Todo 1"},
{id: "todo2", text: "Todo 2"}
}

let items =
todoList
->Belt.List.toArray
->Belt.List.map((todo) => {
<li key={todo.id}>
{React.string(todo.text)}
</li>
})

<div> {React.array(items)} </div>
```

We use `Belt.List.toArray` to convert our list to an array before creating our `array<React.element>`. Please note that using `list` has performance impact due to extra conversion costs.

In 99% 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.

218 changes: 218 additions & 0 deletions pages/docs/react/latest/beyond-jsx.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
---
title: Beyond JSX
description: "Details on how to use ReScript and React without JSX"
canonical: "/docs/react/latest/beyond-jsx"
---

# Beyond JSX

<Intro>

JSX is a syntax sugar that allows us to use React components in an HTML like manner. A component needs to adhere to certain interface conventions, otherwise it can't be used in JSX. This section will go into detail on how the JSX transformation works and what React APIs are used underneath.

</Intro>

**Note:** This section requires knowledge about the low level apis for [creating elements](./elements-and-jsx#creating-elements-from-component-functions), such as `React.createElement` or `ReactDOMRe.createDOMElementVariadic`.

> **Note:** This page assumes your `bsconfig.json` to be set to `"reason": { "react-jsx": 3 }` to apply the right JSX transformations.

## Component Types

A plain React component is defined as a `('props) => React.element` function. You can also express a component more efficiently with our shorthand type `React.component('props)`.

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
// Plain function type
type friendComp =
({"name": string, "online": bool}) => React.element;

// Equivalent to
// ({"padding": string, "children": React.element}) => React.element
type containerComp =
React.component({
"padding": string,
"children": React.element
});
```
The types above are pretty low level (basically the JS representation of a React component), but since ReScript React has its own ways of defining React components in a more language specific way, let's have a closer look on the anatomy of such a construct.

## JSX Component Interface

A ReScript React component needs to be a (sub-)module with a `make` and `makeProps` function to be usable in JSX. To make things easier, we provide a `@react.component` decorator to create those functions for you:

<CodeTab labels={["Decorated", "Expanded"]}>

```res
module Friend = {
@react.component
let make = (~name: string, ~children) => {
<div>
{React.string(name)}
children
</div>
}
}
```
```res
module Friend = {
[@bs.obj]
external makeProps: (
~name: string,
~children: 'children,
~key: string=?,
unit) => {. "name": string, "children": 'children'} = "";

let make = (props: {. "name": string, "children": 'children}) => {
// React element creation from the original make function
}
}
```

</CodeTab>

In the expanded output:

- `makeProps`: A function that receives multiple labeled arguments (according to prop names) and returns the value that is consumed by make(props)
- `make`: A converted `make` function that complies to the component interface `(props) => React.element`

**Note:** The `makeProps` function will also always contain a `~key` prop.

### Special Case React.forwardRef

The `@react.component` decorator also works for `React.forwardRef` calls:


<CodeTab labels={["Decorated", "Expanded"]}>

```res
module FancyInput = {
@react.component
let make = React.forwardRef((~className=?, ~children, ref_) =>
<div>
// use ref_ here
</div>
)
}
```

```res
// Simplified Output
module FancyInput = {
@bs.obj
external makeProps: (
~className: 'className=?,
~children: 'children,
~key: string=?,
~ref: 'ref=?,
unit,
) => {"className": option<'className>, "children": 'children} = ""

let make =
(~className=?, ~children) => ref_ => ReactDOMRe.createDOMElementVariadic("div", [])

let make = React.forwardRef(
(props: {"className": option<'className>, "children": 'children}, ref_,) => {
make(
~className=props["className"],
~children=props["children"],
ref_)
})
}
```

</CodeTab>

As shown in the expanded output above, our decorator desugars the function passed to `React.forwardRef` in the same manner as a typical component `make` function. It also creates a `makeProps` function with a `ref` prop, so we can use it in our JSX call (`<FancyInput ref=.../>`).

So now that we know how the ReScript React component transformation works, let's have a look on how ReScript transforms our JSX constructs.

## JSX Under the Hood

Whenever we are using JSX with a custom component ("capitalized JSX"), we are actually using `React.createElement` to create a new element. Here is an example of a React component without children:

<CodeTab labels={["JSX", "Without JSX"]}>

```res
<Friend name="Fred" age=1 />
```
```res
React.createElement(Friend.make, Friend.makeProps(~name="Fred", ~age=1, ()))
```
```js
React.createElement(Playground$Friend, { name: "Fred", age: 20 });
```

</CodeTab>

As you can see, it uses `Friend.make` and `Friend.makeProps` to call the `React.createElement` API. In case you are providing children, it will use `React.createElementVariadic` instead (which is just a different binding for `React.createElement`):

<CodeTab labels={["JSX", "Without JSX", "JS Output"]}>

```res
<Container width=200>
{React.string("Hello")}
{React.string("World")}
</Container>
```

```res
React.createElementVariadic(
Container.make,
Container.makeProps(~width=200, ~children=React.null, ()),
[{React.string("Hello")}, {React.string("World")}],
)
```

```js
React.createElement(Container, { width: 200, children: null }, "Hello", "World");
```

</CodeTab>

Note that the `~children=React.null` prop has no relevance since React will only care about the children array passed as a third argument.


### Dom Elements

"Uncapitalized JSX" expressions are treated as DOM elements and will be converted to `ReactDOMRe.createDOMElementVariadic` calls:

<CodeTab labels={["JSX", "Without JSX", "JS Output"]}>

```res
<div title="test"/>
```

```res
ReactDOMRe.createDOMElementVariadic("div", ~props=ReactDOMRe.domProps(~title="test", ()), [])
```

```js
React.createElement("div", { title: "test" });
```

</CodeTab>

The same goes for uncapitalized JSX with children:

<CodeTab labels={["JSX", "Without JSX", "JS Output"]}>

```res
<div title="test">
<span/>
</div>
```

```res
ReactDOMRe.createDOMElementVariadic(
"div",
~props=ReactDOMRe.domProps(~title="test", ()),
[ReactDOMRe.createDOMElementVariadic("span", [])],
)
```

```js
React.createElement("div", { title: "test" }, React.createElement("span", undefined));
```

</CodeTab>
Loading