` pattern translates into ReasonReact.
-
-## Callback Without State Update
-
-Two scenarios.
-
-### Without Reading From `self`
-
-_Reminder: `self` is ReasonReact's `this`. It's a record that contains things like `state`, `send` and others._
-
-If you're just forwarding a callback prop onto your child, you'd do exactly the same thing you'd have done in ReactJS:
-
-```reason
-let component = /* ... */;
-
-let make = (~name, ~onClick, _children) => {
- ...component,
- render: (self) =>
-};
-```
-
-No surprise here. Since Reason's JSX has [punning syntax](https://reasonml.github.io/docs/en/jsx.html#punning), that `button` will format into ``.
-
-Similarly, to pre-process a value before sending it back to the component's owner:
-
-```reason
-let component = /* ... */;
-
-let make = (~name, ~onClick, _children) => {
- let click = (event) => onClick(name); /* pass the name string up to the owner */
- {
- ...component,
- render: (self) =>
- }
-};
-```
-
-### Reading From `self`
-
-To access `state`, `send` and the other items in `self` from a callback, you **need** to wrap the callback in an extra layer called `self.handle`:
-
-```reason
-let component = /* ... */;
-
-let make = (~name, ~onClick, _children) => {
- let click = (event, self) => {
- onClick(event);
- Js.log(self.ReasonReact.state);
- };
- {
- ...component,
- initialState: /* ... */,
- render: (self) =>
- }
-};
-```
-
-**Note** how your `click` callback now takes the extra argument `self`. Formally, `self.handle` expects a callback that
-
-- accepts the **single** payload you'd normally directly pass to e.g. `handleClick`,
-- plus the argument `self`,
-- returns "nothing" (aka, `()`, aka, `unit`).
-
-**Note 2**: sometimes you might be forwarding `handle` to some helper functions. Pass the whole `self` instead and **annotate it**. This avoids a complex `self` record type behavior. See [Record Field `send`/`handle` Not Found](record-field-send-handle-not-found.md).
-
-#### Explanation
-
-In reality, `self.handle` is just a regular function accepting two arguments, the first being the callback in question, and the second one being the payload that's intended to be passed to the callback.
-
-Get it? Through Reason's natural language-level currying, we usually only ask you to pass the first argument. This returns a new function that takes in the second argument and executes the function body. The second argument being passed by the caller, aka the component you're rendering!
-
-#### Callback Receiving Multiple Arguments
-
-Sometimes, the component you're calling is from JavaScript (using the [ReasonReact<->ReactJS interop](interop.md)), and its callback prop asks you to pass a callback that receives more than one argument. In ReactJS, it'd look like:
-
-```javascript
-handleSubmit: function(username, password, event) {
- this.setState(...)
-}
-...
-
-```
-
-You cannot write such `handleSubmit` in ReasonReact, as `handle` expects to wrap around a function that only takes **one** argument. Here's the workaround:
-
-```reason
-let handleSubmitEscapeHatch = (username, password, event) =>
- self.handle(
- (tupleOfThreeItems, self) => doSomething(tupleOfThreeItems, self),
- (username, password, event),
- );
-...
-
-```
-
-Basically, you write a normal callback that:
-
-- takes those many arguments from the JS component callback prop,
-- packs them into a tuple and call `self.handle`,
-- pass to `handle` the usual function that expects a single argument,
-- finish calling `self.handle` by passing the tuple directly yourself.
-
-## Callback With State Update
-
-You can't update state in `self.handle`; you need to use `self.send` instead. See the next section.
diff --git a/docs/children.md b/docs/children.md
deleted file mode 100644
index 0fe7bec4a..000000000
--- a/docs/children.md
+++ /dev/null
@@ -1,135 +0,0 @@
----
-title: Children
----
-
-
-
-ReasonReact children are like ReactJS children, except we provide more bells and whistles that leverages the language itself.
-
-Let's say people are using your component like so: ``. In the `Animation` component, you'd want to constrain the `children` being passed to you (the `div` here) as a single child. Aka, you'd like to error on this: ``.
-
-ReactJS provides such helper:
-
-```js
-// inside Animation.js
-render: () => {
- return React.Children.only(this.children);
-}
-```
-
-Or, maybe you made a Layout component and accepts exactly two children (e.g. ``):
-
-```js
-// Layout.js
-render: () => {
- if (React.Children.count(this.props.children) !== 2) {
- // ... error
- }
- return (
-
- );
-}
-```
-
-Or maybe you mandate a children callback:
-
-```js
-
-render: () => {
- return React.Children.only(this.props.children('hello'));
-}
-```
-
-As an author of these JS components, you can only hope that the user provided the right children type to you, and throw an error for them if not. In ReasonReact, `children` prop is typed and prevents bad usage from the consumer. This seems pretty natural, once you realize that `children` props **is basically like any other prop**! Yes, you can pass tuple, variant, record and others to children! This also mean we don't have to provide the equivalent `React.Children.*` utils in ReasonReact at all!
-
-However, due to syntactical and interoperability reasons, we do have some small restrictions on `children`:
-
-- DOM elements such as `div`, `span` and others mandate the `children` you pass to be `array(reactElement)`. For example, `
[1, 2, 3]
` or ` Some(10) ` don't work, since the `list` and the `option` are forwarded to the underlying ReactJS `div`/`span` and such data types don't make sense in JS.
-- User-defined elements, by default, also accept an array (of anything). This is due to a rather silly constraint of the JSX syntax, described below.
-
-## Syntax Constraint
-
-_Technically_, if you've written a component that accepts a tuple as `children`:
-
-```reason
-/* MyForm.re */
-type tuple2Children = (ReasonReact.reactElement, ReasonReact.reactElement);
-
-let make = (children: tuple2Children) => {
- ...component,
- render: _self => {
-
- {fst(children)}
-
- {snd(children)}
-
- }
-};
-```
-
-Then you can expect the user to pass you a tuple:
-
-```reason
- (, )
-```
-
-This, however, will give you a type error:
-
-```
-This has type:
- array('a)
-But somewhere wanted:
- tuple2Children (defined as (ReasonReact.reactElement, ReasonReact.reactElement))
-```
-
-It means that `MyForm` is expecting the tuple, but you're giving `array` instead! What's happening? Well, look at what JSX is transformed into:
-
-```reason
- a b
- a
-```
-
-These actually become:
-
-```reason
-ReasonReact.element(
- MyLayout.make([|a, b|])
-);
-ReasonReact.element(
- MyLayout.make([|a|])
-);
-```
-
-See how the second `MyLayout`'s children is also wrapped in an array? We can't special-case ` a ` to pass `a` without the array wrapping (it'd give even more confusing errors). Since children is usually an array, we've decided to always wrap it with an array, no matter how many items we visually see in the JSX. But what if you really want to pass unwrapped data?
-
-### Children Spread
-
-Just use ` ...a `. This will simply transform into `ReasonReact.element(MyLayout.make(a))`, aka without the array wrapping.
-
-#### Tips & Tricks
-
-Here are some use-cases for children spread + Reason built-in data structures:
-
-- A layout component that mandates a tuple of 2 react element and shows them side-by-side:
-
- ```reason
- ...(, )
- ```
-
-- A component that mandates a callback, no need for ReactJS' `React.Children.only` runtime check:
-
- ```reason
- ...((name) => )
- ```
-
-- A layout helper that accepts a variant of `TwoRows | ThreeRows | FourColumns`
-
- ```reason
- ...(ThreeRows(, child2, child3))
- ```
diff --git a/docs/clone-element.md b/docs/clone-element.md
index 8806735c0..a32f7a5b4 100644
--- a/docs/clone-element.md
+++ b/docs/clone-element.md
@@ -2,17 +2,13 @@
title: cloneElement
---
-
+Signature: `let cloneElement: (React.element, ~props: Js.t({..})=?, 'anyChildrenType) => React.element`
-Signature: `let cloneElement: (reactElement, ~props: Js.t({..})=?, 'anyChildrenType) => reactElement`
-
-Same as ReactJS' [cloneElement](https://reactjs.org/docs/react-api.html#cloneelement). However, adding extra props to a ReasonReact component doesn't make sense; you'd use a [**render prop**](https://reactjs.org/docs/render-props.html). Therefore, `ReasonReact.cloneElement` is only used in edge-cases to convert over existing code.
+Same as ReactJS' [cloneElement](https://reactjs.org/docs/react-api.html#cloneelement). However, adding extra props to a ReasonReact component doesn't make sense; you'd use a [**render prop**](https://reactjs.org/docs/render-props.html). Therefore, `ReasonReact.cloneElement` is only used in edge-cases.
```reason
let clonedElement =
- ReasonReact.cloneElement(
+ React.cloneElement(
,
~props={"payload": 1},
[||]
diff --git a/docs/components.md b/docs/components.md
index 2a667aa13..936b86723 100644
--- a/docs/components.md
+++ b/docs/components.md
@@ -23,7 +23,7 @@ let make = (~name) => {
This snippet is doing quite a bit! The first thing you might notice is the decorator attribute above the definition. `[@react.component]` tells ReasonReact that you're writing a component with named args syntax (`~name`), but that you would like to compile it into a function that takes a JS object as props which is how React works. Concretely, this attribute will generate code for you that looks like this:
```reason
-[@bs.obj]
+[@mel.obj]
external makeProps: (~name: 'name, ~key: string=?, unit) => {. "name": 'name} = "";
let make = (Props) => {
@@ -39,7 +39,7 @@ let make = (Props) => {
};
```
-It has added a new function called `makeProps` which uses [`[@bs.obj]`](https://melange.re/v2.0.0/communicate-with-javascript/#using-jst-objects) to create your props object. This function gets compiled away by Melange and will be replaced by object literals when used.
+It has added a new function called `makeProps` which uses [`[@mel.obj]`](https://melange.re/v4.0.0/communicate-with-javascript#using-js-t-objects) to create your props object. This function gets compiled away by Melange and will be replaced by object literals when used.
### A note on `children`
@@ -113,7 +113,7 @@ Reason also always opts for the safest form of a given hook as well. So `React.u
## Hand-writing components
-You don't need to use the `[@react.component]` declaration to write components. Instead you can write a pair of `foo` and `fooProps` functions such that `type fooProps: 'a => props and foo: props => React.element` and these will always work as React components! This works with your own version of [`[@bs.obj]`](https://melange.re/v2.0.0/communicate-with-javascript/#using-jst-objects), [`[bs.deriving abstract]`](https://melange.re/v2.0.0/communicate-with-javascript/#convert-records-into-abstract-types), or any other function that takes named args and returns a single props structure.
+You don't need to use the `[@react.component]` declaration to write components. Instead you can write a pair of `foo` and `fooProps` functions such that `type fooProps: 'a => props and foo: props => React.element` and these will always work as React components! This works with your own version of [`[@mel.obj]`](https://melange.re/v4.0.0/communicate-with-javascript#using-js-t-objects), [`[bs.deriving abstract]`](https://melange.re/v4.0.0/communicate-with-javascript#using-external-functions), or any other function that takes named args and returns a single props structure.
## Interop
@@ -122,7 +122,7 @@ You don't need to use the `[@react.component]` declaration to write components.
The make function above is a normal React component, you can use it today with code like:
```js
-const MyComponent = require('./path/to/Component.bs.js').make;
+const MyComponent = require('./path/to/Component.js').make;
```
@@ -134,21 +134,21 @@ It also works seamlessly with [`[@genType]`](https://github.com/cristianoc/genTy
Using a component written in JS requires a single external to annotate the types it takes.
```reason
-[@bs.module "./path/to/Component.js"][@react.component]
+[@mel.module "./path/to/Component.js"][@react.component]
external make: (~name: string) => React.element = "default";
```
This `[@react.component]` annotation will, again, generate both `make` and `makeProps` functions for you with the correct types. Here's an example of what this desugars to without `[@react.component]`:
```reason
-[@bs.obj]
+[@mel.obj]
external makeProps: (~name: 'name, ~key: string=?, unit) => {. "name": 'name} = "";
-[@bs.module "./path/to/Component.js"]
+[@mel.module "./path/to/Component.js"]
external make: ({. "name": string}) => React.element = "default";
```
-**Note on `default`:** to understand what `default` means, see [the Melange docs on ES6](https://melange.re/v2.0.0/communicate-with-javascript/#default-es6-values).
+**Note on `default`:** to understand what `default` means, see [the Melange docs on ES6](https://melange.re/v4.0.0/communicate-with-javascript#default-es6-values).
## Component Naming
diff --git a/docs/context-mixins.md b/docs/context.md
similarity index 55%
rename from docs/context-mixins.md
rename to docs/context.md
index 43d48ed75..c0a3d43f0 100644
--- a/docs/context-mixins.md
+++ b/docs/context.md
@@ -1,22 +1,37 @@
---
-title: Context & Mixins
+title: Context
---
-## Context
-
In order to use React's context, you need to create two things:
+
1. The context itself
-2. A context provider component.
+2. A context provider component
```reason
-/** ContextProvider.re */
+/** as a separate file: ContextProvider.re */
+
+// 1. The context itself
let themeContext = React.createContext("light");
+// 2. The provider
include React.Context; // Adds the makeProps external
let make = React.Context.provider(themeContext);
```
-That will give you a `ContextProvider` component you can use in your application later on. You'll do this like you'd normally would in any React application.
+```reason
+/** or inside a any other module */
+
+// 1. The context itself
+let themeContext = React.createContext("light");
+
+// 2. The provider component
+module ContextProvider = {
+ include React.Context; // Adds the makeProps external
+ let make = React.Context.provider(themeContext);
+};
+```
+
+That will give you a `ContextProvider` component you can use in your application later on, by wrapping any component with `ContextProvider`, to have access to the context value inside the component tree. To know more about Context, check the [official React documentation](https://legacy.reactjs.org/docs/context.html) and [when to use it](https://react.dev/learn/passing-data-deeply-with-context).
```reason
/** App.re */
@@ -29,7 +44,7 @@ let make = () =>
```
-Also, you can consume the context by using the `React.useContext` hook
+Then you can consume the context by using the `React.useContext` hook
```reason
/** ComponentToConsumeTheContext.re */
@@ -43,7 +58,7 @@ let make = () => {
## Binding to an external Context
-Binding to a Context defined in a JS file holds no surprises.
+Binding to a Context defined in a JavaScript file holds no surprises.
```js
/** ComponentThatDefinesTheContext.js */
@@ -62,6 +77,3 @@ let make = () => {
theme->React.string
}
```
-
-## Mixins
-ReasonReact doesn't support ReactJS mixins. Composing normal functions is a good alternative.
diff --git a/docs/creation-props-self.md b/docs/creation-props-self.md
deleted file mode 100644
index bcd591815..000000000
--- a/docs/creation-props-self.md
+++ /dev/null
@@ -1,101 +0,0 @@
----
-title: Creation, Props & Self
----
-
-
-
-_The documentation assumes relative familiarity with ReactJS._
-
-ReasonReact doesn't use/need classes. The component creation API gives you a plain record, whose fields (like `render`) you can override.
-
-The component template is created through `ReasonReact.statelessComponent("TheComponentName")`. The string being passed is for debugging purposes (the equivalent of ReactJS' [`displayName`](https://reactjs.org/docs/react-component.html#displayname)).
-
-As an example, here's the file `Greeting.re`:
-
-```reason
-let component = ReasonReact.statelessComponent("Greeting");
-```
-
-**In ReactJS**, you'd create a component class and call it through JSX which transforms into `React.createElement(myClass, {prop1: 'hello'})` under the hood. **In ReasonReact**, instead of passing the whole "class" (aka component template) into a hypothetical `ReasonReact.createElement` function, you'd instead declare a `make` function:
-
-```reason
-/* still in Greeting.re */
-let component = ReasonReact.statelessComponent("Greeting");
-
-let make = (~name, _children) => {
- ...component, /* spread the template's other defaults into here */
- render: _self =>
{ReasonReact.string(name)}
-};
-```
-
-The `make` function is what's called by ReasonReact's JSX, described later. For now, the JSX-less way of calling & rendering a component is:
-
-```reason
-ReasonReact.element(Greeting.make(~name="John", [||])) /* the `make` function in the module `Greeting` */
-/* equivalent to */
-```
-
-`make` asks you to return the component record created above. You'd override a few fields, such as the familiar `render`, `initialState`, `didMount`, etc., all described later.
-
-**Note**: do **not** inline `let component` into the `make` function body like the following!
-
-```reason
-let make = _children => {
- ...(ReasonReact.statelessComponent("Greeting")),
- render: self => blabla
-}
-```
-
-Since `make` is called at every JSX invocation, you'd be accidentally creating a fresh new component every time.
-
-## Props
-
-Props are just the labeled arguments of the `make` function, seen above. They can also be optional and/or have defaults, e.g. `let make = (~name, ~age=?, ~className="box", _children) => /* ... */`.
-
-The last prop **must** be `children`. If you don't use it, simply ignore it by naming it `_` or `_children`. Names starting with underscore don't trigger compiler warnings if they're unused.
-
-**The prop name cannot be `ref` nor `key`**. Those are reserved, just like in ReactJS.
-
-Following that example, you'd call that component in another file through ``. `className`, if omitted, defaults to "box". `age` defaults to `None`. If you'd like to explicitly pass `age`, simply do so: ``.
-
-### Neat Trick with Props Forwarding
-
-Sometimes in ReactJS, you're being given a prop from the owner that you'd like to forward directly to the child:
-
-```
-
-```
-
-This is a source of bugs, because `this.props.age` might be accidentally changed to a nullable number while `Foo` doesn't expect it to be so, or vice-versa; it might be nullable before, and now it's not and `Foo` is left with a useless null check somewhere in the render.
-
-In Reason, if you want to explicitly pass an optional `myAge` (whose type is `option(int)`, aka `None | Some(int)`), the following wouldn't work:
-
-```reason
-
-```
-
-Because the `age` label expects a normal `int` when you do call `Foo` with it, not an `option(int)`! Naively, you'd be forced to solve this like so:
-
-```reason
-switch (myAge) {
-| None =>
-| Some(nonOptionalAge) =>
-}
-```
-
-Cumbersome. Fortunately, here's a better way to explicitly pass an optional value:
-
-```reason
-
-```
-
-It says "I understand that `myAge` is optional and that I should either use the label `age` and pass an `int`, or not use the label at all. But I'd like to forward an `option`al value explicitly to avoid the verbose `switch`".
-
-This isn't a JSX trick we've made up; it's just a language feature! See the section on "Explicitly Passed Optional" in the [Reason docs](https://reasonml.github.io/docs/en/function.html#explicitly-passed-optional).
-
-## `self`
-
-You might have seen the `render: (self) => ...` part in `make`. The concept of JavaScript `this` doesn't exist in ReasonReact (but can exist in Reason, since it has an optional object system); the `this` equivalent is called `self`. It's a record that contains `state`, `handle` and `send`, which we pass around to the lifecycle events, `render` and a few others, when they need the bag of information. These concepts will be explained later on.
-
diff --git a/docs/custom-class-component-property.md b/docs/custom-class-component-property.md
deleted file mode 100644
index d5cfd6693..000000000
--- a/docs/custom-class-component-property.md
+++ /dev/null
@@ -1,44 +0,0 @@
----
-title: Custom Class/Component Property
----
-
-Your ReactJS component might have extra properties attached onto it:
-
-```js
-class HelloMessage extends React.Component {
- static getRandomNumber() {
- return 4;
- };
-
- getAnswerToLife() {
- return 42;
- };
-
- render() {
- ...
- }
-}
-```
-
-Since ReasonReact components are created from a record (which has fixed fields), you can't attach arbitrary fields onto it. Here are the solutions.
-
-## Static Class Property
-
-Just export a standalone value/function:
-
-```reason
-let component = /* ... */;
-let make = /* ... */;
-
-let getRandomNumber = () => 4;
-```
-
-Keep it simple!
-
-## Instance (Component) Property
-
-If the component property/method doesn't refer to the component instance (aka `this` in JS), then it can just be a static class property, in which case you should just export a normal `let` value.
-
-If the component _does_ conceptually refer to `this`, then still try to turn it into a normal `let` value that takes in a normal argument instead of reading into the component's `this`.
-
-_If this part's unclear, or if it doesn't work in your case, please [file an issue](https://github.com/reasonml/reason-react/issues/new)_!
diff --git a/docs/usedebounce-custom-hook.md b/docs/custom-hooks.md
similarity index 82%
rename from docs/usedebounce-custom-hook.md
rename to docs/custom-hooks.md
index 7e4cc13fd..70c090245 100644
--- a/docs/usedebounce-custom-hook.md
+++ b/docs/custom-hooks.md
@@ -1,9 +1,8 @@
---
-title: A Custom useDebounce Hook
+title: Custom Hooks
---
```reason
-/* this is a hook that takes 2 arguments */
let useDebounce = (value, delay) => {
let (debouncedValue, setDebouncedValue) = React.useState(_ => value);
diff --git a/docs/dom.md b/docs/dom.md
index d170ce296..2838f3354 100644
--- a/docs/dom.md
+++ b/docs/dom.md
@@ -4,16 +4,22 @@ title: Working with DOM
## ReactDOM
-ReasonReact's ReactDOM module is called `ReactDOMRe`. The module exposes helpers that work with familiar ReactJS idioms.
+ReasonReact's ReactDOM module is called `ReactDOM`. The module exposes helpers that work with familiar ReactJS idioms
-- `render` : `(React.element, Dom.element) => unit`
-- `unmountComponentAtNode` : `Dom.element => unit`
-- `hydrate` : `(React.element, Dom.element) => unit`
-- `createPortal` : `(React.element, Dom.element) => React.element`
+- `ReactDOM.querySelector` : `string => option(Dom.element)`
+- `ReactDOM.Client.createRoot` : `Dom.element => Client.root`
+- `ReactDOM.Client.render` : `(Client.root, React.element) => unit`
+- `ReactDOM.Client.hydrateRoot` : `(Dom.element, React.element) => Client.root`
+- `ReactDOM.createPortal` : `(React.element, Dom.element) => React.element`
+- `ReactDOM.unmountComponentAtNode` : `Dom.element => unit`
+
+More info about `ReactDOM` can be found in the [official ReactDOM documentation](https://react.dev/reference/react-dom).
## ReactDOMServer
-ReasonReact's equivalent `ReactDOMServer` exposes:
+ReasonReact's equivalent of `ReactDOMServer` from `react-dom/server` exposes
- `renderToString` : `React.element => string`
- `renderToStaticMarkup` : `React.element => string`
+
+More info about `ReactDOMServer` can be found in the [official React documentation](https://react.dev/reference/react-dom/server).
diff --git a/docs/element-type-is-invalid.md b/docs/element-type-is-invalid.md
index 77c36a27c..8ead7f249 100644
--- a/docs/element-type-is-invalid.md
+++ b/docs/element-type-is-invalid.md
@@ -15,13 +15,13 @@ This likely means that:
This is a common mistake. Please see Melange's [Import an ES6 Default Value](https://melange.re/v2.0.0/communicate-with-javascript/#default-es6-values). Aka, instead of:
```reason
-[@bs.module] external myJSReactClass: ReasonReact.reactClass = "./myJSReactClass";
+[@mel.module] external myJSReactClass: ReasonReact.reactClass = "./myJSReactClass";
```
Use:
```reason
-[@bs.module "./myJSReactClass"] external myJSReactClass: ReasonReact.reactClass = "default";
+[@mel.module "./myJSReactClass"] external myJSReactClass: ReasonReact.reactClass = "default";
```
Remember that Reason doesn't have runtime type errors! So it _must_ have meant that your binding was written wrongly.
diff --git a/docs/error-boundaries.md b/docs/error-boundaries.md
index d1c7b2428..074b87031 100644
--- a/docs/error-boundaries.md
+++ b/docs/error-boundaries.md
@@ -34,7 +34,7 @@ class MyErrorBoundary extends React.Component {
```
-Given ReasonReact does not bind to ReactJS class API, we're providing a lightweight component that does that just for you: `ReasonReactErrorBoundary`.
+We're providing a lightweight component that does that just for you: `ReasonReactErrorBoundary`.
```reason
ReactEvent.Form.target##value;
+event->React.Event.Form.target##value;
```
-More info in the [inline docs](https://github.com/reasonml/reason-react/blob/main/src/ReactEvent.rei#L1).
+More info in the [inline docs](https://github.com/reasonml/reason-react/blob/main/src/React.rei#L1) on the interface file.
diff --git a/docs/functional-component.md b/docs/functional-component.md
deleted file mode 100644
index 92ee07693..000000000
--- a/docs/functional-component.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Functional Component
----
-
-You can use a function as a component in ReactJS. In ReasonReact, this is not yet possible (soon!); though you can obviously just call the function without using the JSX syntax: `MyList.make(~className="foo", [||])`.
diff --git a/docs/gentype.md b/docs/gentype.md
deleted file mode 100644
index a73589352..000000000
--- a/docs/gentype.md
+++ /dev/null
@@ -1,48 +0,0 @@
----
-title: Gentype & Typescript
----
-
-[Gentype](https://github.com/cristianoc/genType) is a library that automatically generates idiomatic bindings between Reason and JavaScript: either vanilla or typed with TypeScript/FlowType.
-
-If all your components are written in Typescript for example, this is a great way of supporting full type safety in both directions.
-
-Installing it includes running `yarn add --dev gentype` and creating a basic `gentypeconfig` in your `bsconfig.json` file:
-
-```
-"gentypeconfig": {
- "language": "typescript",
- "shims": {},
- "debug": {
- "all": false,
- "basic": false
- }
-}
-```
-
-Read more [here](https://github.com/cristianoc/genType#installation)
-
-### Basic Greeting Component
-
-```reason
-/* Greeting.re */
-
-[@genType]
-[@react.component]
-let make = (~message) => {
-
{React.string(message)}
-}
-```
-
-Here's how you import that component into your Typescript project. As you can see, there's a new file generated with a `.gen` extension. Then, everything works as is!
-
-```ts
-/* App.tsx */
-
-import { Greeting } from './Greeting.gen';
-
-// ..
-
-export default function App() {
- return
-}
-```
diff --git a/docs/importing-js-into-reason.md b/docs/importing-js-into-reason.md
index 9b177a93e..493f72f61 100644
--- a/docs/importing-js-into-reason.md
+++ b/docs/importing-js-into-reason.md
@@ -16,7 +16,7 @@ export default function Greeting({ name }) {
/* App.re */
module Greeting = {
- [@bs.module "./MyJavascriptFile.js"] [@react.component]
+ [@mel.module "./MyJavascriptFile.js"] [@react.component]
external make: (~name: string) => React.element = "default";
};
@@ -45,7 +45,7 @@ as its signature is concerned. To bind to it, we just make the prop optional.
```reason
module Greeting = {
- [@bs.module "./Greeting.js"] [@react.component]
+ [@mel.module "./Greeting.js"] [@react.component]
external make: (~name: string=?) => React.element = "default";
};
```
@@ -62,11 +62,11 @@ wrap it with our own component to add the logic.
```reason
module GreetingJs = {
- [@bs.module "./Greeting.js"] [@react.component]
+ [@mel.module "./Greeting.js"] [@react.component]
external make: (~name: string) => React.element = "default";
};
-module Greeting = {
+module Greeting = {
[@react.component]
let make = (~name="Peter") => ;
};
diff --git a/docs/instance-variables.md b/docs/instance-variables.md
deleted file mode 100644
index 4f99f503c..000000000
--- a/docs/instance-variables.md
+++ /dev/null
@@ -1,44 +0,0 @@
----
-title: Instance Variables
----
-
-
-
-A common pattern in ReactJS is to attach extra variables onto a component's spec:
-
-```
-const Greeting = React.createClass({
- intervalId: null,
- componentDidMount: () => this.intervalId = setInterval(...),
- render: ...
-});
-```
-
-In reality, this is nothing but a thinly veiled way to mutate a component's "state", without triggering a re-render. ReasonReact asks you to correctly put these instance variables into your component's `state`, into Reason [`ref`s](https://reasonml.github.io/docs/en/mutation.html).
-
-```reason
-type state = {
- someRandomState: option(string),
- intervalId: ref(option(Js.Global.intervalId))
-};
-
-let component = /* ... */; /* remember, `component` needs to be close to `make`, and after `state` type declaration! */
-
-let make = (_children) => {
- ...component,
- initialState: () => {
- someRandomState: Some("hello"),
- intervalId: ref(None),
- },
- didMount: ({state}) => {
- /* mutate the value here */
- state.intervalId := Some(Js.Global.setInterval(/* ... */));
- },
- render: /* ... */
-};
-```
-
-**All your instance variables (subscriptions, refs, etc.) must be in state fields marked as a `ref`**. Don't directly use a `mutable` field on the state record, use an immutable field pointing to a Reason `ref`. Why such constraint? To prepare for concurrent React which needs to manage side-effects & mutations more formally. More details [here](https://reasonml.github.io/try.html?reason=C4TwDgpgBAzlC8UDeBDAXFAlgO2AGlgHsBbCASWxmBWwGMIA1FAJw2YgDMAKHYASgC+AbgBQAegBUUbIQDuUCWJEAbCMFjVg0REhFQo6KAEY8eoqQpUa9JqyjtuAJj4jh4qRGIBXZSi1QAAystAINaehg4DmYSKFplQhgvdgA6KAAVAAtodhQAEyhCDkDgiFDiTABzTPUAI2grVShakChgbLbMUixgAHI4CAAPCFovLTyMRXcoDi9sZFKBBAA+GbmoLj4VqAAlCBQYQmw9lFpgFIBVMDy-aCQUh9KCWt8lqdV1UscEZAeUp4MGAAzG5JM0xlBvJpMEdCsUgpoytIIBA8nBgIRmjlOKozqisPMEbdHKEqJhlMoAIQKJSlf4kciUah0RgsKBoRAAFlEYJgmUIPgK9SgnJpIgAUjAUglKlwvvSLEzrKzmAA9Pg8qTtTBwWQC5R5bC9dR65gAaywxVk0DymNAkAMCFQGF4BChKBeDQZlmZNhYLtwbzEQA) if you're ever interested.
-
diff --git a/docs/interop.md b/docs/interop.md
deleted file mode 100644
index dfba755ff..000000000
--- a/docs/interop.md
+++ /dev/null
@@ -1,156 +0,0 @@
----
-title: Talk to Existing ReactJS Code
----
-
-
-
-## Project Setup
-
-You can reuse the _same_ bsb setup (that you might have seen [here](installation.md#bsb))! Aka, put a `bsconfig.json` at the root of your ReactJS project:
-
-```json
-{
- "name": "my-project-name",
- "reason": {"react-jsx" : 2},
- "sources": [
- "my_source_folder"
- ],
- "package-specs": [{
- "module": "commonjs",
- "in-source": true
- }],
- "suffix": ".bs.js",
- "namespace": true,
- "bs-dependencies": [
- "reason-react"
- ],
- "refmt": 3
-}
-```
-
-This will build Reason files in `my_source_folder` (e.g. `reasonComponent.re`) and output the JS files (e.g. `reasonComponent.bs.js`) alongside them.
-
-Then add `bs-platform` to your package.json (`npm install --save-dev bs-platform` or `yarn add --dev bs-platform`):
-
-```json
-"scripts": {
- "start": "bsb -make-world -w"
-},
-"devDependencies": {
- "bs-platform": "^2.1.0"
-},
-"dependencies": {
- "react": "^15.4.2",
- "react-dom": "^15.4.2",
- "reason-react": "^0.3.1"
-}
-...
-```
-
-Running `npm start` (or alias it to your favorite command) starts the `bsb` build watcher. **You don't have to touch your existing JavaScript build configuration**!
-
-## Usage
-
-A ReasonReact record component **is not** a ReactJS component. We provide hooks to communicate between the two.
-
-Whether you're using an existing ReactJS component or providing a ReasonReact component for consumption on the JS side, you need to establish the type of the JS props you'd convert from/to, by using [BuckleScript's `bs.deriving abstract`](https://bucklescript.github.io/docs/en/object.html):
-
-```reason
-[@bs.deriving abstract]
-type jsProps = {
- /* some example fields */
- className: string,
- /* `type` is reserved in Reason. use `type_` and make it still compile to the
- JS key `type` */
- [@bs.as "type"] type_: string,
- value: Js.nullable(int),
-};
-```
-
-This will generate the getters and the JS object creation function (of the same name, `jsProps`) you'll need.
-
-**Note**: you do **not** declare `ref` and `key` (the two special ReactJS "props"). We handle that for you, just like ReactJS does. They're not really props.
-
-### ReasonReact using ReactJS
-
-Easy! Since other Reason components only need you to expose a `make` function, fake one up:
-
-```reason
-[@bs.module] external myJSReactClass: ReasonReact.reactClass = "./myJSReactClass";
-
-let make = (~className, ~type_, ~value=?, children) =>
- ReasonReact.wrapJsForReason(
- ~reactClass=myJSReactClass,
- ~props=jsProps(
- ~className,
- ~type_,
- ~value=Js.Nullable.fromOption(value),
- ),
- children,
- );
-```
-
-`ReasonReact.wrapJsForReason` is the helper we expose for this purpose. It takes in:
-
-- The `reactClass` you want to wrap
-- The `props` js object you'd create through the generated `jsProps` function from the `jsProps` type you've declared above (with values **properly converted** from Reason data structures to JS)
-- The mandatory children you'd forward to the JS side.
-
-`props` is mandatory. If you don't have any to pass, pass `~props=Js.Obj.empty()` instead.
-
-**Note**: if your app successfully compiles, and you see the error "element type is invalid..." in your console, you might be hitting [this mistake](element-type-is-invalid.md).
-
-### ReactJS Using ReasonReact
-
-Eeeeasy. We expose a helper for the other direction, `ReasonReact.wrapReasonForJs`:
-
-```reason
-let component = ...;
-let make ...;
-
-[@bs.deriving abstract]
-type jsProps = {
- name: string,
- age: Js.nullable(int),
-};
-
-let jsComponent =
- ReasonReact.wrapReasonForJs(~component, jsProps =>
- make(
- ~name=jsProps->nameGet,
- ~age=?Js.Nullable.toOption(jsProps->ageGet),
- [||],
- )
- );
-```
-
-The function takes in:
-
-- The labeled reason `component` you've created
-- A function that, given the JS props, asks you to call `make` while passing in the correctly converted parameters (`bs.deriving abstract` above generates a field accessor for every record field you've declared).
-
-> Note the `jsProps->nameGet` and `jsProps->ageGet` part. This is a getter generated by `bs.deriving abstract`. Documentations [here](https://bucklescript.github.io/docs/en/object#record-mode).
-
-You'd assign the whole thing to the name `jsComponent`. The JS side can then import it:
-
-```
-var MyReasonComponent = require('./myReasonComponent.bs').jsComponent;
-// make sure you're passing the correct data types!
-
-```
-
-**Note**: if you'd rather use a **default import** on the JS side, you can export such default from BuckleScript/ReasonReact:
-
-```reason
-let default = ReasonReact.wrapReasonForJs(...)
-```
-
-and then import it on the JS side with:
-
-```
-import MyReasonComponent from './myReasonComponent.bs';
-```
-
-BuckleScript default exports **only** works when the JS side uses ES6 import/exports. [More info here](https://bucklescript.github.io/docs/en/import-export.html#export-an-es6-default-value).
diff --git a/docs/jsx-2.md b/docs/jsx-2.md
deleted file mode 100644
index 3ebe4a195..000000000
--- a/docs/jsx-2.md
+++ /dev/null
@@ -1,127 +0,0 @@
----
-title: JSX (Old, Version 2)
----
-
-
-
-In addition to the [primary JSX transform](jsx.md), you can also use JSX to call older Record components. To do so you can add a `[@bs.config {jsx: 2}];` configuration attribute at the top of a file. Within that file all JSX tags will desugar to the form described here.
-
-## Uncapitalized
-
-```reason
-
{child1} {child2}
-```
-
-transforms into
-
-```reason
-ReactDOMRe.createElement("div", ~props=ReactDOMRe.props(~foo=bar, ()), [|child1, child2|]);
-```
-
-which compiles to the JS code:
-
-```js
-React.createElement('div', {foo: bar}, child1, child2)
-```
-
-Prop-less `` transforms into
-
-```reason
-ReactDOMRe.createElement("div", [||]);
-```
-
-Which compiles to
-
-```js
-React.createElement('div', undefined)
-```
-
-**Note that `ReactDOMRe.createElement` is intended for internal use by the JSX transform**. For escape-hatch scenarios, use `ReasonReact.createDomElement` instead, as outlined in the [children section](children.md).
-
-## Capitalized
-
-```reason
- {child1} {child2}
-```
-
-transforms into
-
-```reason
-ReasonReact.element(
- ~key=a,
- ~ref=b,
- MyReasonComponent.make(~foo=bar, ~baz=qux, [|child1, child2|])
-);
-```
-
-Prop-less `` transforms into
-
-```reason
-ReasonReact.element(MyReasonComponent.make([||]));
-```
-
-The `make` above is exactly the same `make` function you've seen in the previous section.
-
-**Note how `ref` and `key` have been lifted out of the JSX call into the `ReasonReact.element` call**. `ref` and `key` are reserved in ReasonReact, just like in ReactJS. **Don't** use them as props in your component!
-
-## Fragment
-
-```reason
-<> child1 child2 >;
-```
-
-transforms into
-
-```reason
-ReactDOMRe.createElement(ReasonReact.fragment, [|child1, child2|]);
-```
-
-Which compiles to
-
-```js
-React.createElement(React.Fragment, undefined, child1, child2);
-```
-
-## Children
-
-ReasonReact children are **fully typed**, and you can pass any data structure to it (as long as the receiver component permits it). When you write:
-
-```reason
-
-```
-
-You're effectively passing the array `[| , |]` to `MyReasonComponent`'s children. But this also means that the following wouldn't work:
-
-```reason
-let theChildren = [| , |];
- theChildren
-```
-
-Because this actually translates to:
-
-```reason
-let theChildren = [| , |];
-ReasonReact.element(
- MyReasonComponent.make([|theChildren|])
-);
-```
-
-Which wraps the already wrapped `theChildren` in another layer of array. To solve this issue, Reason has a special [children spread syntax](https://reasonml.github.io/docs/en/jsx.html#children-spread):
-
-```reason
-let theChildren = [| , |];
- ...theChildren
-```
-
-This simply passes `theChildren` without array wrapping. It becomes:
-
-```reason
-let theChildren = [| , |];
-ReasonReact.element(
- MyReasonComponent.make(theChildren)
-);
-```
-
-For more creative ways of leveraging Reason's type system, data structures and performance to use `children` to its full potential, see the [Children section](children.md)!
diff --git a/docs/lifecycles.md b/docs/lifecycles.md
deleted file mode 100644
index 3872be0d8..000000000
--- a/docs/lifecycles.md
+++ /dev/null
@@ -1,101 +0,0 @@
----
-title: Lifecycles
----
-
-
-
-ReasonReact supports the familiar ReactJS lifecycle events.
-
-```reason
-didMount: self => unit
-
-willReceiveProps: self => state
-
-shouldUpdate: oldAndNewSelf => bool
-
-willUpdate: oldAndNewSelf => unit
-
-didUpdate: oldAndNewSelf => unit
-
-willUnmount: self => unit
-```
-
-Note:
-
-- We've dropped the `component` prefix from all these.
-- `willReceiveProps` asks for the return type to be `state`, not `update state` (i.e. not `NoUpdate/Update/SideEffects/UpdateWithSideEffects`). We presume you'd always want to update the state in this lifecycle. If not, simply return the previous `state` exposed in the lifecycle argument.
-- `didUpdate`, `willUnmount` and `willUpdate` don't allow you to return a new state to be updated, to prevent infinite loops.
-- `willMount` is unsupported. Use `didMount` instead.
-- `didUpdate`, `willUpdate` and `shouldUpdate` take in a **`oldAndNewSelf` record**, of type `{oldSelf: self, newSelf: self}`. These two fields are the equivalent of ReactJS' `componentDidUpdate`'s `prevProps/prevState/` in conjunction with `props/state`. Likewise for `willUpdate` and `shouldUpdate`.
-
-If you need to update state in a lifecycle event, simply `send` an action to `reducer` and handle it correspondingly: `self.send(DidMountUpdate)`.
-
-**Some new lifecycle methods act differently**. Described below.
-
-## Access next or previous props: `retainedProps`
-
-One pattern that's sometimes used in ReactJS is accessing a lifecycle event's `prevProps` (`componentDidUpdate`), `nextProps` (`componentWillUpdate`), and so on. ReasonReact doesn't automatically keep copies of previous props for you. We provide the `retainedProps` API for this purpose:
-
-```reason
-type retainedProps = {message: string};
-
-let component = ReasonReact.statelessComponentWithRetainedProps("RetainedPropsExample");
-
-let make = (~message, _children) => {
- ...component,
- retainedProps: {message: message},
- didUpdate: ({oldSelf, newSelf}) =>
- if (oldSelf.retainedProps.message !== newSelf.retainedProps.message) {
- /* do whatever sneaky imperative things here */
- Js.log("props `message` changed!")
- },
- render: (_self) => /* ... */
-};
-```
-
-We expose `ReasonReact.statelessComponentWithRetainedProps` and `ReasonReact.reducerComponentWithRetainedProps`. Both work like their ordinary non-retained-props counterpart, and require you to specify a new field, `retainedProps` (of whatever type you'd like) in your component's spec in `make`.
-
-## `willReceiveProps`
-
-Traditional ReactJS `componentWillReceiveProps` takes in a `nextProps`. We don't have `nextProps`, since those are simply the labeled arguments in `make`, available to you in the scope. To access the _current_ props, however, you'd use the above `retainedProps` API:
-
-```reason
-type state = {someToggle: bool};
-
-let component = ReasonReact.reducerComponentWithRetainedProps("MyComponent");
-
-let make = (~name, _children) => {
- ...component,
- initialState: () => {someToggle: false},
- /* just like state, the retainedProps field can return anything! Here it retained the `name` prop's value */
- retainedProps: name,
- willReceiveProps: (self) => {
- if (self.retainedProps === name) {
- /* ... */
- /* previous ReactJS logic would be: if (props.name === nextProps.name) */
- };
- /* ... */
- }
-};
-```
-
-## `willUpdate`
-
-ReactJS' `componentWillUpdate`'s `nextProps` is just the labeled arguments in `make`, and "current props" (aka `this.props`) is the props you've copied into `retainedProps`, accessible via `{oldSelf}`:
-
-```reason
-{
- ...component,
- willUpdate: ({oldSelf, newSelf}) => /* ... */
-}
-```
-
-## `didUpdate`
-
-ReactJS' `prevProps` is what you've synced in `retainedProps`, under `oldSelf`.
-
-## `shouldUpdate`
-
-ReactJS' `shouldComponentUpdate` counterpart.
diff --git a/docs/react-ref.md b/docs/react-ref.md
deleted file mode 100644
index e8c654e44..000000000
--- a/docs/react-ref.md
+++ /dev/null
@@ -1,53 +0,0 @@
----
-title: React Ref
----
-
-
-
-_Not to be confused with Reason `ref`, the language feature that enables mutation_.
-
-A ReasonReact `ref` would be just another instance variable. You'd type it as `ReasonReact.reactRef` if it's attached to a custom component, and `Dom.element` if it's attached to a React DOM element.
-
-```reason
-type state = {
- isOpen: bool,
- mySectionRef: ref(option(ReasonReact.reactRef))
-};
-
-let setSectionRef = (theRef, {ReasonReact.state}) => {
- state.mySectionRef := Js.Nullable.toOption(theRef);
- /* wondering about Js.Nullable.toOption? See the note below */
-};
-
-let component = ReasonReact.reducerComponent("MyPanel");
-
-let make = (~className="", _children) => {
- ...component,
- initialState: () => {
- isOpen: false,
- mySectionRef: ref(None),
- },
- reducer: /* ... */,
- render: (self) =>
-};
-```
-
-Attaching to a React DOM element looks the same: `state.mySectionRef = {myDivRef: Js.Nullable.toOption(theRef)}`.
-
-**Note** how [ReactJS refs can be null](https://github.com/facebook/react/issues/9328#issuecomment-298438237). Which is why `theRef` and `myDivRef` are converted from a [JS nullable](https://bucklescript.github.io/docs/en/null-undefined-option.html) to an OCaml `option` (Some/None). When you use the ref, you'll be forced to handle the null case through a `switch`, which prevents subtle errors!
-
-**You must follow the instanceVars convention in the previous section for ref**.
-
-ReasonReact ref only accept callbacks. The string `ref` from ReactJS is deprecated.
-
-We also expose an escape hatch `ReasonReact.refToJsObj` of type `ReasonReact.reactRef => Js.t {..}`, which turns your ref into a JS object you can freely use; **this is only used to access ReactJS component class methods**.
-
-```reason
-let handleClick = (event, self) =>
- switch (self.state.mySectionRef^) {
- | None => ()
- | Some(r) => ReasonReact.refToJsObj(r)##someMethod(1, 2, 3) /* I solemnly swear that I am up to no good */
- };
-```
diff --git a/docs/reasonreactcompat.md b/docs/reasonreactcompat.md
deleted file mode 100644
index 5e7a9e332..000000000
--- a/docs/reasonreactcompat.md
+++ /dev/null
@@ -1,222 +0,0 @@
----
-title: ReasonReactCompat: migrating to 0.7.0 and JSX v3
----
-
-In version 0.7.0, two large changes were introduced:
-
-- Support for React hooks
-- A new JSX version (v3)
-
-You can read more in the release [blog post](https://reasonml.github.io/reason-react/blog/2019/04/10/react-hooks).
-
-To enable the progressive migrations of existing ReasonReact applications to the new API and JSX version, a new module called `ReasonReactCompat` was added to ReasonReact.
-
-## React and ReasonReact modules
-
-`ReasonReactCompat` is a bridge between two modules:
-
-- `ReasonReact`: contains the now frozen API that was used to implement components in v0.6.0 and earlier using JSX v2.
-- `React`: the module that contains the hooks-compatible API, that is available since v0.7.0 and uses JSX v3.
-
-JSX versions 2 and 3 are coupled with the corresponding modules `ReasonReact` and `React`. In other words: a component created using `ReasonReact` module will require JSX v2, and a component built using `React` module will require JSX v3.
-
-For clarity and brevity, in the rest of the section we will refer to each group as follows:
-- `ReasonReact` components (< 0.7.0) that use JSX v2, as **v2 components**
-- `React` components (>= 0.7.0) that use JSX v3, as **v3 components**.
-
-## `wrapReactForReasonReact`: Wrapping a v3 component to be used from a v2 component
-
-In most cases, we will want to migrate our application to use v3 components (see the section about migration strategies [below](https://reasonml.github.io/reason-react/docs/en/reasonreactcompat.html#migrating-an-application-to-v070-and-jsx-v3)).
-
-For example, a v2 component `Banner` might need to use a v3 component `Image`. `Image` can be made compatible by leveraging `ReasonReactCompat.wrapReactForReasonReact`:
-
-```reason
-/* In Image.re */
-[@bs.config {jsx: 3}];
-
-[@react.component]
-let make = (~src) => ;
-
-module Jsx2 = {
- let component = ReasonReact.statelessComponent("Image");
- /* `children` is not labelled, as it is a regular parameter in version 2 of JSX */
- let make = (~src, children) =>
- ReasonReactCompat.wrapReactForReasonReact(
- make,
- makeProps(~src, ()),
- children,
- );
-};
-```
-
-Then, `Image` can be used from `Banner`:
-
-```reason
-/* In Banner.re */
-let component = ReasonReact.statelessComponent("Banner");
-
-let make = _children => {
- ...component,
- render: _self => ,
-};
-```
-
-### Component with `children`
-
-Some components pass down `children` transparently, like:
-
-```reason
-/* In SomeButton.re */
-[@react.component]
-let make = (~children) => ;
-```
-
-If this kind of components need to expose a `Jsx2` module for backwards compatibility, like seen above, you might run into errors like:
-
-```
-This expression has type array('a)
-but an expression was expected of type
-ReasonReact.reactElement = React.element
-```
-
-In these cases, it is helpful to wrap `children` with `React.array` inside the `Jsx2` compatibility module, like:
-
-```reason
-/* In SomeButton.re */
-module Jsx2 = {
- let component = ReasonReact.statelessComponent("SomeButton");
- let make = children => {
- let children = React.array(children);
- ReasonReactCompat.wrapReactForReasonReact(
- make,
- makeProps(~children, ()),
- children,
- );
- };
-};
-```
-
-The reason behind those errors is that version 3 of JSX doesn't automatically wrap the `children` passed to an element in an array, like version 2 used to do. Using `React.array` in the `Jsx2` module is a way to provide a consistent behavior for usages of the component in both versions of the JSX transform.
-
-## `wrapReasonReactForReact`: Wrapping a v2 component to be used from a v3 component
-
-Sometimes we might need to make the translation the other way around: wrap a v2 component to be used from a v3 component. For example, if we are using some ReasonReact library that has not been updated yet to be compatible with the latest version.
-
-In these cases, we can use `ReasonReact.wrapReasonReactForReact`.
-
-Let's say we have a v2 component `Text` that needs to be used from a v3 component `Heading`:
-
-```reason
-/* In Text.re */
-let component = ReasonReact.statelessComponent("Text");
-
-let make = (~text, _children) => {
- ...component,
- render: _self => {ReasonReact.string(text)} ,
-};
-```
-
-We can follow the same approach as above and add a `Jsx3` module to the same file:
-
-```reason
-/* Still in Text.re */
-module Jsx3 = {
- [@bs.obj] external makeProps: (~text: string, unit) => _ = "";
- let make =
- ReasonReactCompat.wrapReasonReactForReact(
- ~component, (reactProps: {. "text": string}) =>
- make(~text=reactProps##text, [||])
- );
-};
-```
-
-### Component with `children`
-
-If the v2 component we want to migrate uses `children` we have to take some extra steps to convert it.
-
-For example, let's say we have a `List` v2 component:
-
-```reason
-/* List.re */
-let component = ReasonReact.statelessComponent("List");
-
-let make = (~visible, children) => {
- ...component,
- render: _self => visible ?
...children
: ReasonReact.null,
-};
-```
-
-The `Jsx3` compat module will look like:
-
-```reason
-/* Still in List.re */
-module Jsx3 = {
- [@bs.obj]
- external makeProps: (~visible: bool, ~children: 'children=?, unit) => _ = "";
- let make =
- ReasonReactCompat.wrapReasonReactForReact(
- ~component,
- (
- reactProps: {
- .
- "visible": bool,
- "children": 'children,
- },
- ) =>
- make(
- ~visible=reactProps##visible,
- reactProps##children
- ->Js.Undefined.toOption
- ->Belt.Option.mapWithDefault([||], c => [|c|]),
- )
- );
-};
-```
-
-Because v3 components using `List.Jsx3` can decide whether to pass `children` or not, we have to account for those cases by setting `children` as optional labelled argument in `makeProps`. After that, we also have convert `children` to an array inside `make` before passing control over to the v2 implementation.
-
-## Migrating an application to v0.7.0 and JSX v3
-
-There are many ReasonReact applications, so it is hard to define "The One True" migration strategy for them all.
-
-Depending on the size and nature of your application there are two options available to migrate your application from 0.6.0 to 0.7.0.
-
-### Application level
-
-By adding `{"reason": {"react-jsx": 3}` in your [`bsconfig.json`](https://bucklescript.github.io/docs/en/build-configuration.html#reason-refmt).
-
-This approach requires that all components in the application must be made compatible with version 3 of JSX at once, so it will be a better fit for smaller apps with a reduced number of components, where all of them can be migrated to version 3 in one fell swoop.
-
-### File level
-
-For larger applications, it might not be possible to migrate all components at once. In these cases, a per-file migration is also possible.
-
-A file can be configured to use version 3 of the transform by adding `[@bs.config {jsx: 3}];` at the top of the file.
-
-The per-file configuration allows to mix, in the same application, components compatible with either of both versions of the JSX transforms. However, the restriction is that all the components used in a file will have to be compatible with the JSX version specified for that file.
-
-For example, if a file contains the following code:
-
-```reason
-/* User.re */
-[@bs.config {jsx: 3}];
-
-[@react.component]
-let make = (~id) => {
-
-
- ;
-};
-```
-
-Then `Profile` and `UserDetails` components will have to be compatible with the version 3 of JSX. Or alternatively, if they are using version 2, they can be wrapped with the function `ReasonReactCompat.wrapReasonReactForReact`, as seen in [the section above](https://reasonml.github.io/reason-react/docs/en/reasonreactcompat.html#wrapreasonreactforreact-wrapping-a-v2-component-to-be-used-from-a-v3-component).
-
-#### From primitives to more complex components
-
-As all usages of any component in a file need to be migrated to the same version of JSX, one natural way to tackle large migrations at the file level is to start converting the most primitive components to version 3, as they generally render elements of a reduced number of components, or host elements like ``. Once the most simple components are done, one can proceed with the most complex ones.
-
-Once all components are using version 3, there is no more need to keep the `[@bs.config {jsx: 3}];` annotations at the top of each file, and they can be replaced by bumping the JSX version in the `bsconfig.json` file to `{"reason": {"react-jsx": 3}` for the whole application.
-
-### Upgrade script
-
-A migration script [is provided](https://github.com/chenglou/upgrade-reason-react#installation) to facilitate the task to convert components to version 3. It will wrap existing ReasonReact components as if they are Hooks components. This script will not attempt to re-write your logic as hooks because this is often a complicated process and it is not guaranteed to be correct. Please always inspect and test the work of the migration script to make sure it does what you are expecting.
diff --git a/docs/record-field-send-handle-not-found.md b/docs/record-field-send-handle-not-found.md
deleted file mode 100644
index f232a1b05..000000000
--- a/docs/record-field-send-handle-not-found.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Record Field send/handle Not Found
----
-
-Do you see a type error related to this? This might mean that you've passed `self` to a helper function of your render, and it used it like so: `
self.send(Click)} />`. This is because the record can't be found in the scope of the file. Just annotate it: `
self.ReasonReact.send(Click)} />`.
-
-More info [here](https://reasonml.github.io/docs/en/record.html#record-needs-an-explicit-definition).
diff --git a/docs/render.md b/docs/render.md
deleted file mode 100644
index 155aa9f47..000000000
--- a/docs/render.md
+++ /dev/null
@@ -1,27 +0,0 @@
----
-title: Render
----
-
-
-
-`render` needs to return a `ReasonReact.reactElement`: ``, ``, etc. Render takes the argument `self`:
-
-```reason
-/* ... */
- render: (self) =>
-/* ... */
-```
-
-What if you want to return `null` from a `render`? Or pass a string to a DOM component like `div` which only allows `ReasonReact.reactElement`s?
-
-In ReactJS, you can easily do: `
hello
`, `
{1}
`, `
{null}
`, etc. In Reason, the type system restricts you from passing arbitrary data like so; you can only return `ReasonReact.reactElement` from `render`.
-
-Fortunately, we special-case a few special elements of the type `ReasonReact.reactElement`:
-
-- `ReasonReact.null`: This is your `null` equivalent for `render`'s return value. Akin to `return null` in ReactJS render.
-
-- `ReasonReact.string`: Takes a string and converts it to a `reactElement`. You'd use `
{ReasonReact.string(string_of_int(10))}
` to display an int.
-
-- `ReasonReact.array`: Takes an array and converts it to a `reactElement`.
diff --git a/docs/router-2.md b/docs/router-2.md
deleted file mode 100644
index f2ab5b559..000000000
--- a/docs/router-2.md
+++ /dev/null
@@ -1,128 +0,0 @@
----
-title: Router
----
-
-ReasonReact comes with a router! We've leveraged the language and library features in order to create a router that's:
-
-- The simplest, thinnest possible.
-- Easily pluggable anywhere into your existing code.
-- Performant and tiny.
-
-[Here's the documented public interface](https://github.com/reasonml/reason-react/blob/main/src/ReasonReactRouter.rei), repeated here:
-
-- `ReasonReactRouter.push(string)`: takes a new path and update the URL.
-- `ReasonReactRouter.replace(string)`: like `push`, but replaces the current URL.
-- `ReasonReactRouter.watchUrl(f)`: start watching for URL changes. Returns a subscription token. Upon url change, calls the callback and passes it the `ReasonReactRouter.url` record.
-- `ReasonReactRouter.unwatchUrl(watcherID)`: stop watching for url changes.
-- `ReasonReactRouter.dangerouslyGetInitialUrl()`: get `url` record outside of `watchUrl`. Described later.
-- `ReasonReactRouter.useUrl(~serverUrl)`: only usable in the new [function component api](https://reasonml.github.io/docs/en/components).
-
-## Match a Route
-
-**There's no API**! `watchUrl` gives you back a `url` record of the following shape:
-
-```reason
-type url = {
- /* path takes window.location.pathname, like "/book/title/edit" and turns it into `["book", "title", "edit"]` */
- path: list(string),
- /* the url's hash, if any. The # symbol is stripped out for you */
- hash: string,
- /* the url's query params, if any. The ? symbol is stripped out for you */
- search: string
-};
-```
-
-
-
-So the url `www.hello.com/book/10/edit?name=Jane#author` is given back as:
-
-```reason
-{
- path: ["book", "10", "edit"],
- hash: "author",
- search: "name=Jane"
-}
-```
-
-At this point, you can simply pattern match your way to glory!
-
-```reason
-let watcherID = ReasonReact.Router.watchUrl(url => {
- switch (url.path) {
- | ["book", id, "edit"] => handleBookEdit(id)
- | ["book", id] => getBook(id)
- | ["book", id, _] => noSuchBookOperation()
- | [] => showMainPage()
- | ["shop"] | ["shop", "index"] => showShoppingPage()
- | ["shop", ...rest] =>
- /* e.g. "shop/cart/10", but let "cart/10" be handled by another function */
- nestedMatch(rest)
- | _ => showNotFoundPage()
- }
-});
-
-/* some time later */
-ReasonReact.Router.unwatchUrl(watcherID);
-```
-
-So you can match a path, match a subset of a path, defer part of a matching to a nested logic, etc.
-
-### Tips & Tricks
-
-Notice that this is just normal [pattern matching](https://reasonml.github.io/docs/en/pattern-matching.html). You can combine it with other features, such as tuple + ReasonReact features like [subscriptions](subscriptions-helper.md) and reducer:
-
-```reason
-let component = ReasonReact.reducerComponent("TodoApp");
-
-let make = _children => {
- ...component,
- reducer: (action, state) =>
- switch (action) {
- /* router actions */
- | ShowAll => ReasonReact.Update({...state, nowShowing: AllTodos})
- | ShowActive => /* ... */
- /* todo actions */
- | ChangeTodo(text) => /* ... */
- },
- didMount: self => {
- let watcherID = ReasonReact.Router.watchUrl(url => {
- switch (url.hash, MyAppStatus.isUserLoggedIn) {
- | ("active", _) => self.send(ShowActive)
- | ("completed", _) => self.send(ShowCompleted)
- | ("shared", true) => self.send(ShowShared)
- | ("shared", false) when isSpecialUser => /* handle this state please */
- | ("shared", false) => /* handle this state please */
- | _ => self.send(ShowAll)
- }
- });
- self.onUnmount(() => ReasonReact.Router.unwatchUrl(watcherID));
- },
- render: ...
-}
-```
-
-## Directly Get a Route
-
-In one specific occasion, you might want to take hold of a `url` record _outside_ of `watchUrl`. For example, if you've put `watchUrl` inside a component's `didMount` so that a URL change triggers a component state change, you might also want the initial state to be dictated by the URL.
-
-In other words, you'd like to read from the `url` record once at the beginning of your app logic. We expose `dangerouslyGetInitialUrl()` for this purpose.
-
-**Note**: the reason why we label it as "dangerous" is to remind you **not** to read this `url` in any arbitrary component's e.g. `render`, since that information might be out of date if said component doesn't also contain a `watchUrl` subscription that re-renders the component when the URL changes. Aka, please only use `dangerouslyGetInitialUrl` alongside `watchUrl`.
-
-## Push a New Route
-
-From anywhere in your app, just call e.g. `ReasonReact.Router.push("/books/10/edit#validated")`. This will trigger a URL change (without a page refresh) and `watchUrl`'s callback will be called again.
-
-We might provide better facilities for typed routing + payload carrying in the future!
-
-**Note**: because of browser limitations, changing the URL through JavaScript (aka `pushState`) **cannot** be detected. The solution is to change the URL then fire a `"popState"` event. This is what `Router.push` does, and what the event `watchUrl` listens to. So if, for whatever reason (e.g. incremental migration), you want to update the URL outside of `Router.push`, just do `window.dispatchEvent(new Event('popState'))`.
-
-## Design Decisions
-
-We always strive to lower the performance and learning overhead in ReasonReact, and our router design's no different. The entire implementation, barring browser features detection, is around 20 lines. The design might seem obvious in retrospect, but to arrive here, we had to dig back into ReactJS internals & future proposals to make sure we understood the state update mechanisms, the future context proposal, lifecycle ordering, etc. and reject some bad API designs along the way. It's nice to arrive at such an obvious solution!
-
-The API also doesn't dictate whether matching on a route should return a component, a state update, or a side-effect. Flexible enough to slip into existing apps.
-
-Performance-wise, a JavaScript-like API tends to use a JS object of route string -> callback. We eschewed that in favor of pattern-matching, since the latter in Reason does not allocate memory, and is compiled to a fast jump table in C++ (through the JS JIT). In fact, the only allocation in the router matching is the creation of the `url` record!
diff --git a/docs/send-handle-callbacks-having-incompatible-types.md b/docs/send-handle-callbacks-having-incompatible-types.md
deleted file mode 100644
index 5fb62d0f4..000000000
--- a/docs/send-handle-callbacks-having-incompatible-types.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: send/handle callbacks having Incompatible Types
----
-
-You've probably passed `self.send` to a helper function that uses this `send` reference twice. For complex reasons this doesn't type; you'd have to pass in the whole `self` to the helper.
diff --git a/docs/state-actions-reducer.md b/docs/state-actions-reducer.md
deleted file mode 100644
index 840c995aa..000000000
--- a/docs/state-actions-reducer.md
+++ /dev/null
@@ -1,162 +0,0 @@
----
-title: State, Actions & Reducer
----
-
-
-
-Finally, we're getting onto stateful components!
-
-ReasonReact stateful components are like ReactJS stateful components, except with the concept of "reducer" (like [Redux](http://redux.js.org)) built in. If that word doesn't mean anything to you, just think of it as a state machine. If _that_ word does mean something to you, just think: "Woah this is great".
-
-To declare a stateful ReasonReact component, instead of `ReasonReact.statelessComponent("MyComponentName")`, use `ReasonReact.reducerComponent("MyComponentName")`.
-
-Here's a complete, working, stateful ReasonReact component. We'll refer to it later on.
-
-```reason
-/* State declaration */
-type state = {
- count: int,
- show: bool,
-};
-
-/* Action declaration */
-type action =
- | Click
- | Toggle;
-
-/* Component template declaration.
- Needs to be **after** state and action declarations! */
-let component = ReasonReact.reducerComponent("Example");
-
-/* greeting and children are props. `children` isn't used, therefore ignored.
- We ignore it by prepending it with an underscore */
-let make = (~greeting, _children) => {
- /* spread the other default fields of component here and override a few */
- ...component,
-
- initialState: () => {count: 0, show: true},
-
- /* State transitions */
- reducer: (action, state) =>
- switch (action) {
- | Click => ReasonReact.Update({...state, count: state.count + 1})
- | Toggle => ReasonReact.Update({...state, show: !state.show})
- },
-
- render: self => {
- let message =
- "You've clicked this " ++ string_of_int(self.state.count) ++ " times(s)";
-
;
- },
-};
-```
-
-## `initialState`
-
-ReactJS' `getInitialState` is called `initialState` in ReasonReact. It takes `unit` and returns the state type. The state type could be anything! An int, a string, a ref or the common record type, which you should declare **right before the `reducerComponent` call**:
-
-```reason
-type state = {count: int, show: bool};
-
-let component = ReasonReact.reducerComponent("Example");
-
-let make = (~onClick, _children) => {
- ...component,
- initialState: () => {count: 0, show: true},
- /* ... other fields */
-};
-
-```
-
-Since the props are just the arguments on `make`, feel free to read into them to initialize your state based on them.
-
-## Actions & Reducer
-
-In ReactJS, you'd update the state inside a callback handler, e.g.
-
-```javascript
-{
- /* ... other fields */
- handleClick: function() {
- this.setState({count: this.state.count + 1});
- },
- handleSubmit: function() {
- this.setState(...);
- },
- render: function() {
- return (
-
- );
- }
-}
-```
-
-In ReasonReact, you'd gather all these state-setting handlers into a single place, the component's `reducer`! **Please refer to the first snippet of code on this page**.
-
-**Note**: if you ever see mentions of `self.reduce`, this is the old API. The new API is called `self.send`. The old API's docs are [here](https://github.com/reasonml/reason-react/blob/e17fcb5d27a2b7fb2cfdc09d46f0b4cf765e50e4/docs/state-actions-reducer.md).
-
-A few things:
-
-- There's a user-defined type called **`action`**, named so by convention. It's a variant of all the possible state transitions in your component. _In state machine terminology, this'd be a "token"_.
-- A user-defined `state` type, and an `initialState`. Nothing special.
-- The current `state` value is accessible through `self.state`, whenever `self` is passed to you as an argument of some function.
-- A "**reducer**"! This [pattern-matches](https://reasonml.github.io/docs/en/pattern-matching.html) on the possible actions and specifies what state update each action corresponds to. _In state machine terminology, this'd be a "state transition"_.
-- In `render`, instead of `self.handle` (which doesn't allow state updates), you'd use `self.send`. `send` takes an action.
-
-So, when a click on the dialog is triggered, we "send" the `Click` action to the reducer, which handles the `Click` case by returning the new state that increments a counter. ReasonReact takes the state and updates the component.
-
-**Note**: just like for `self.handle`, sometimes you might be forwarding `send` to some helper functions. Pass the whole `self` instead and **annotate it**. This avoids a complex `self` record type behavior. See [Record Field `send`/`handle` Not Found](record-field-send-handle-not-found.md).
-
-## State Update Through Reducer
-
-Notice the return value of `reducer`? The `ReasonReact.Update` part. Instead of returning a bare new state, we ask you to return the state wrapped in this "update" variant. Here are its possible values:
-
-- `ReasonReact.NoUpdate`: don't do a state update.
-- `ReasonReact.Update(state)`: update the state.
-- `ReasonReact.SideEffects(self => unit)`: no state update, but trigger a side-effect, e.g. `ReasonReact.SideEffects(_self => Js.log("hello!"))`.
-- `ReasonReact.UpdateWithSideEffects(state, self => unit)`: update the state, **then** trigger a side-effect.
-
-### Important Notes
-
-**Please read through all these points**, if you want to fully take advantage of `reducer` and avoid future ReactJS Fiber race condition problems.
-
-- The `action` type's variants can carry a payload: `onClick=(data => self.send(Click(data.foo)))`.
-- Don't pass the whole event into the action variant's payload. ReactJS events are pooled; by the time you intercept the action in the `reducer`, the event's already recycled.
-- `reducer` **must** be pure! Aka don't do side-effects in them directly. You'll thank us when we enable the upcoming concurrent React (Fiber). Use `SideEffects` or `UpdateWithSideEffects` to enqueue a side-effect. The side-effect (the callback) will be executed after the state setting, but before the next render.
-- If you need to do e.g. `ReactEvent.BlablaEvent.preventDefault(event)`, do it in `self.send`, before returning the action type. Again, `reducer` must be pure.
-- Feel free to trigger another action in `SideEffects` and `UpdateWithSideEffects`, e.g. `UpdateWithSideEffects(newState, (self) => self.send(Click))`.
-- If your state only holds instance variables, it also means (by the convention in the instance variables section) that your component only contains `self.handle`, no `self.send`. You still need to specify a `reducer` like so: `reducer: ((), _state) => ReasonReact.NoUpdate`. Otherwise you'll get a `variable cannot be generalized` type error.
-
-### Tip
-
-Cram as much as possible into `reducer`. Keep your actual callback handlers (the `self.send(Foo)` part) dumb and small. This makes all your state updates & side-effects (which itself should mostly only be inside `ReasonReact.SideEffects` and `ReasonReact.UpdateWithSideEffects`) much easier to scan through. Also more ReactJS fiber async-mode resilient.
-
-## Async State Setting
-
-In ReactJS, you could use `setState` inside a callback, like so:
-
-```
-setInterval(() => this.setState(...), 1000);
-```
-
-In ReasonReact, you'd do something similar:
-
-```reason
-Js.Global.setInterval(() => self.send(Tick), 1000)
-```
diff --git a/docs/subscriptions-helper.md b/docs/subscriptions-helper.md
deleted file mode 100644
index 9c69fb520..000000000
--- a/docs/subscriptions-helper.md
+++ /dev/null
@@ -1,81 +0,0 @@
----
-title: Subscriptions Helper
----
-
-In a large, heterogeneous app, you might often have legacy or interop data sources that come from outside of the React/ReasonReact tree, or a timer, or some browser event handling. You'd listen and react to these changes by, say, updating the state.
-
-For example, Here's what you're probably doing currently, for setting up a timer event:
-
-```reason
-type state = {
- timerId: ref(option(Js.Global.intervalId))
-};
-
-let component = ReasonReact.reducerComponent("Todo");
-
-let make = _children => {
- ...component,
- initialState: () => {timerId: ref(None)},
- didMount: self => {
- self.state.timerId := Some(Js.Global.setInterval(() => Js.log("hello!"), 1000));
- },
- willUnmount: self => {
- switch (self.state.timerId^) {
- | Some(id) => Js.Global.clearInterval(id);
- | None => ()
- }
- },
- render: /* ... */
-};
-```
-
-Notice a few things:
-
-- This is rather boilerplate-y.
-- Did you use a `ref(option(foo))` type correctly instead of a mutable field, as indicated by the [Instance Variables section](instance-variables.md)?
-- Did you remember to free your timer subscription in `willUnmount`?
-
-For the last point, go search your codebase and see how many `setInterval` you have compared to the amount of `clearInterval`! We bet the ratio isn't 1 =). Likewise for `addEventListener` vs `removeEventListener`.
-
-To solve the above problems and to codify a good practice, ReasonReact provides a helper field in `self`, called `onUnmount`. It asks for a callback of type `unit => unit` in which you free your subscriptions. It'll be called before the component unmounts.
-
-Here's the previous example rewritten:
-
-```reason
-let component = ReasonReact.statelessComponent("Todo");
-
-let make = _children => {
- ...component,
- didMount: self => {
- let intervalId = Js.Global.setInterval(() => Js.log("hello!"), 1000);
- self.onUnmount(() => Js.Global.clearInterval(intervalId));
- },
- render: /* ... */
-};
-```
-
-Now you won't ever forget to clear your timer!
-
-## Design Decisions
-
-**Why not just put some logic in the willUnmount `lifecycle`**? Definitely do, whenever you could. But sometimes, folks forget to release their subscriptions inside callbacks:
-
-```reason
-let make = _children => {
- ...component,
- reducer: (action, state) => {
- switch (action) {
- | Click => ReasonReact.SideEffects(self => {
- fetchAsyncData(result => {
- self.send(Data(result))
- });
- })
- }
- },
- render: /* ... */
-};
-```
-
-If the component unmounts and _then_ the `fetchAsyncData` calls the callback, it'll accidentally call `self.send`. Using `let cancel = fetchAsyncData(...); self.onUnmount(() => cancel())` is much easier.
-
-**Note**: this is an **interop helper**. This isn't meant to be used as a shiny first-class feature for e.g. adding more flux stores into your app (for that purpose, please use our [local reducer](state-actions-reducer.md#actions-reducer)). Every time you use `self.onUnmount`, consider it as a simple, pragmatic and performant way to talk to the existing world.
diff --git a/docs/tailwind-css.md b/docs/tailwind-css.md
index 5e86e402b..38416b136 100644
--- a/docs/tailwind-css.md
+++ b/docs/tailwind-css.md
@@ -2,7 +2,7 @@
title: Styling: Tailwind CSS
---
-[Tailwind CSS](https://tailwindcss.com) is a new CSS framework that is rapidly
+[Tailwind CSS](https://tailwindcss.com) is a CSS framework that is rapidly
growing in popularity. It's completely customizable and lightweight, making it
a perfect companion to React. If you're not familiar with Tailwind, we recommend
checking out [their docs](https://tailwindcss.com/#what-is-tailwind) for
@@ -40,43 +40,3 @@ let make = () =>
which gives us the following UI:
-
-## tailwind-ppx
-
-Often times when you're writing with Tailwind and ReasonReact, you may find
-yourself wondering why the UI isn't working like it should, only to find out you
-had a typo in a class name. For example,
-
-```reason
-
- ...
-
-```
-
-Wouldn't it be nice to get some validation _while_ you're writing the Tailwind
-classes? Better yet, how about preventing your code from even compiling if the
-classes aren't correct? Well, enter
-[`tailwind-ppx`](https://github.com/dylanirlbeck/tailwind-ppx): a compile-time
-validator for Tailwind CSS. Using this PPX, you can get immediate compiler
-errors if you happen to misspell class names, along with a nice suggestion of
-what you may have meant to write!
-
-```reason
-
/* ERROR: Class name not found: flex-ro. Did you mean flex-row? */
- ...
-
-```
-
-Moreover, in a large codebase where components may have many class names, you
-may find yourself duplicating some class names. `tailwind-ppx` solves this
-issue, too!
-
-```reason
-
/* ERROR: Duplicate class name: flex */
- ...
-
-```
-
-Wrapping the class names in a PPX allows for some powerful integrations with
-Tailwind in addition to validation. For more information, check out
-`tailwind-ppx`'s [other features](https://github.com/dylanirlbeck/tailwind-ppx#features)
diff --git a/docs/testing.md b/docs/testing.md
index 73b753784..ae591d9ba 100644
--- a/docs/testing.md
+++ b/docs/testing.md
@@ -2,6 +2,8 @@
title: Testing ReasonReact components
---
+
+
Even though types make your code safer, it doesn't remove the need for testing!
If you want to test your ReasonReact components using your JavaScript testing stack, that's perfectly alright, but if you prefer to write them in Reason, here are some testing frameworks and bindings to make your life easier:
diff --git a/docs/use-state-use-effect.md b/docs/use-state-use-effect.md
deleted file mode 100644
index 4fc9d9fd5..000000000
--- a/docs/use-state-use-effect.md
+++ /dev/null
@@ -1,48 +0,0 @@
----
-title: useState, useEffect in a Form
----
-
-Here's a simple example of how to use React's `useState` with `useEffects`.
-
-```reason
-[@react.component]
-let make = (~label, ~onSubmit) => {
- let (editing, setEditing) = React.useState(() => false);
- let (value, onChange) = React.useState(() => label);
- let onCancel = _evt => setEditing(_ => false);
- let onFocus = event => ReactEvent.Focus.target(event)##select();
-
- React.useEffect1(
- () => {
- onChange(_ => label);
- None
- },
- [|label|],
- );
-
- if (editing) {
- ;
- } else {
- setEditing(_ => true)}>
- value->React.string
- ;
- };
-};
-```
diff --git a/docs/useeffect-hook.md b/docs/useeffect-hook.md
index 395941246..ad07f7907 100644
--- a/docs/useeffect-hook.md
+++ b/docs/useeffect-hook.md
@@ -1,5 +1,5 @@
---
-title: useEffect Hook
+title: useEffect
---
[React.js docs for useEffect](https://reactjs.org/docs/hooks-reference.html#useeffect)
diff --git a/docs/usereducer-hook.md b/docs/usereducer-hook.md
index 177e76f35..f9d4ae13b 100644
--- a/docs/usereducer-hook.md
+++ b/docs/usereducer-hook.md
@@ -1,37 +1,36 @@
---
-title: useReducer Hook
+title: useReducer
---
[React.js docs for useReducer](https://reactjs.org/docs/hooks-reference.html#usereducer)
->useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. useReducer also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.
+> useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. useReducer also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.
```reason
-/* we create a type for the action */
+/* we create a type for the action, but action can be anything */
type action =
- | Tick;
+ | Increment
+ | Decrement;
-/* and do the same for state */
-type state = {count: int};
+/* similarly on 'state', it can be anything. In this case, it's an int */
+let reducer = (state, action) =>
+ switch (action) {
+ | Increment => state + 1
+ | Decrement => state - 1
+ };
[@react.component]
-let make = () => {
- let (state, dispatch) =
- React.useReducer(
- (state, action) =>
- switch (action) {
- | Tick => {count: state.count + 1}
- },
- {count: 0},
- );
+ let make = (~initialValue=0) => {
+ let (state, dispatch) = React.useReducer(reducer, initialValue);
- /* useEffect hook takes 0 arguments hence, useEffect0 */
- React.useEffect0(() => {
- let timerId = Js.Global.setInterval(() => dispatch(Tick), 1000);
- Some(() => Js.Global.clearInterval(timerId));
- });
-
- /* ints need to be converted to strings, that are then consumed by React.string */
-
{React.string(string_of_int(state.count))}
;
-};
+
+
state->React.int
+
+
+ ;
+ };
```
diff --git a/docs/usestate-event-value.md b/docs/usestate-event-value.md
index e3fae88f4..bb71d18ed 100644
--- a/docs/usestate-event-value.md
+++ b/docs/usestate-event-value.md
@@ -31,7 +31,7 @@ let make = () => {
setName(_ => ReactEvent.Form.target(event)##value)
+ onChange={event => setName(_ => React.Event.Form.target(event)##value)
/>;
};
```
@@ -62,7 +62,7 @@ let make = () => {
value={name}
onChange={
event => {
- let value = ReactEvent.Form.target(event)##value;
+ let value = React.Event.Form.target(event)##value;
setName(_ => value)
}
}
diff --git a/docs/usestate-hook.md b/docs/usestate-hook.md
index 7a1f68272..167f9b2d0 100644
--- a/docs/usestate-hook.md
+++ b/docs/usestate-hook.md
@@ -1,25 +1,30 @@
---
-title: useState Hook
+title: useState
---
+The setState function is used to update the state. It accepts a new state value and enqueues a re-render of the component.
+
[React.js docs for useState](https://reactjs.org/docs/hooks-reference.html#usestate)
->The setState function is used to update the state. It accepts a new state value and enqueues a re-render of the component.
+### Usage
-### A Simple Counter Example
+The `useState` hook takes a function that returns the initial state value and returns a tuple with the current state value and a function to update the state.
+
+ReasonReact exposes the `useState` hook with the initialiser function, not with the inmediate value.
```reason
-type action =
- | Tick;
+let useState: (unit => 'state) => ('state, 'state => unit);
+```
-type state = {count: int};
+### A Simple Counter Example
+```reason
[@react.component]
let make = (~initialCount) => {
let (count, setCount) = React.useState(_ => initialCount);
- <>
- {React.string("Count: " ++ string_of_int(count))}
+
+ {React.string("Count: " ++ Int.to_string(count))}
@@ -29,6 +34,80 @@ let make = (~initialCount) => {
- >;
+ ;
};
```
+
+## Using Event values with useState
+
+In ReactJS, it's common to update a component's state based on an event's
+value. Because ReasonReact's `useState` is slightly different than ReactJS,
+directly translating JavaScript components to Reason can lead to a common bug.
+
+```js
+/* App.js */
+function App() {
+ const [name, setName] = React.useState("John");
+ return (
+ setName(event.target.value)}
+ />
+ );
+}
+```
+
+If we convert this component to reason, we may attempt to write this:
+
+```reason
+/* App.re */
+/* WRONG! Don't do this! */
+[@react.component]
+let make = () => {
+ let (name, setName) = React.useState(() => "John");
+ setName(_ => React.Event.Form.target(event)##value)
+ />;
+};
+```
+
+Can you spot the bug?
+
+In the Reason version, the `onChange` callback won't correctly update the state.
+This happens because the callback passed to `setName` is run *after* the `event`
+variable is cleaned up by React, so the `value` field won't exist when it's
+needed.
+
+This isn't actually any different than how events and `useState` hooks work in
+ReactJS when you choose to use a callback with `setName`. The only difference
+is that ReasonReact enforces that we always use callbacks.
+
+## Solution
+
+Fortunately, fixing this bug is simple:
+
+```reason
+/* App.re */
+/* CORRECT! This code is bug-free. 👍 */
+[@react.component]
+let make = () => {
+ let (name, setName) = React.useState(() => "John");
+ {
+ let value = React.Event.Form.target(event)##value;
+ setName(_ => value)
+ }
+ }
+ />;
+};
+```
+
+The key is to extract the `value` from the `event` *before* we send it to
+`setName`. Even if React cleans up the event, we don't lose access to the
+value we need.
diff --git a/docs/working-with-optional-data.md b/docs/working-with-optional-data.md
index c6c40874b..2929c0c0d 100644
--- a/docs/working-with-optional-data.md
+++ b/docs/working-with-optional-data.md
@@ -2,7 +2,11 @@
title: Working with Optional Data
---
-If you're coming from Javascript, optional data can be a real pain in the butt. ReasonML removes a *whole class* of `null` and `undefined` bugs which makes your code WAY safer and easier to write, but it takes some good examples to get you there :smile:
+If you're coming from JavaScript, optional data can be a real pain in the butt. ReasonML removes a *whole class* of `null` and `undefined` bugs which makes your code safer and easier to write, but it takes some good examples to get you there :smile:
+
+ReasonML uses the `option` type to represent optional data. As defined in the standard library [here](https://reasonml.github.io/api/Option.html).
+
+Here there are a few examples of how to work with optional data in ReasonML, using the [Belt](https://melange.re/v4.0.0/api/re/melange/Belt) library from `melange.belt`.
### Accessing Optional Nested Data
diff --git a/src/React.rei b/src/React.rei
index c7664b2ba..2b3fe44e3 100644
--- a/src/React.rei
+++ b/src/React.rei
@@ -565,7 +565,8 @@ module Uncurried: {
};
[@mel.module "react"]
-external startTransition: ([@mel.uncurry] (unit => unit)) => unit = "startTransition";
+external startTransition: ([@mel.uncurry] (unit => unit)) => unit =
+ "startTransition";
[@mel.module "react"]
external useTransition: unit => (bool, callback(callback(unit, unit), unit)) =
@@ -591,11 +592,11 @@ module Event: {
the generic synthetic event. The rest are the specific ones.
In each module, the type `t` commonly means "the type of that module" (OCaml convention). In our case, e.g.
- `ReactEvent.Mouse.t` represents a ReactJS synthetic mouse event. You'd use it to type your props:
+ `React.Event.Mouse.t` represents a ReactJS synthetic mouse event. You'd use it to type your props:
```
type props = {
- onClick: ReactEvent.Mouse.t => unit
+ onClick: React.Event.Mouse.t => unit
};
```
@@ -607,13 +608,13 @@ module Event: {
```
let handleClick = ({state, props}, event) => {
- ReactEvent.Mouse.preventDefault(event);
+ React.Event.Mouse.preventDefault(event);
...
};
let handleSubmit = ({state, props}, event) => {
/* this handler can be triggered by either a Keyboard or a Mouse event; conveniently use the generic
preventDefault */
- ReactEvent.Synthetic.preventDefault(event);
+ React.Event.Synthetic.preventDefault(event);
...
};
@@ -622,8 +623,8 @@ module Event: {
How to translate idioms from ReactJS:
- 1. myMouseEvent.preventDefault() -> ReactEvent.Mouse.preventDefault(myMouseEvent)
- 2. myKeyboardEvent.which -> ReactEvent.Keyboard.which(myKeyboardEvent)
+ 1. myMouseEvent.preventDefault() -> React.Event.Mouse.preventDefault(myMouseEvent)
+ 2. myKeyboardEvent.which -> React.Event.Keyboard.which(myKeyboardEvent)
*/
type synthetic('a);
diff --git a/src/ReactDOMServer.rei b/src/ReactDOMServer.rei
index b967a1465..95a1cd02d 100644
--- a/src/ReactDOMServer.rei
+++ b/src/ReactDOMServer.rei
@@ -4,5 +4,3 @@ external renderToString: React.element => string = "renderToString";
[@mel.module "react-dom/server"]
external renderToStaticMarkup: React.element => string =
"renderToStaticMarkup";
-
-
diff --git a/website/i18n/en.json b/website/i18n/en.json
index de9266ba9..51a3782a8 100644
--- a/website/i18n/en.json
+++ b/website/i18n/en.json
@@ -8,12 +8,6 @@
"adding-data-props": {
"title": "Adding data-* attributes"
},
- "callback-handlers": {
- "title": "Callback Handlers"
- },
- "children": {
- "title": "Children"
- },
"clone-element": {
"title": "cloneElement"
},
@@ -26,14 +20,8 @@
"components": {
"title": "Components"
},
- "context-mixins": {
- "title": "Context & Mixins"
- },
- "creation-props-self": {
- "title": "Creation, Props & Self"
- },
- "custom-class-component-property": {
- "title": "Custom Class/Component Property"
+ "context": {
+ "title": "Context"
},
"dom": {
"title": "Working with DOM"
@@ -50,12 +38,6 @@
"example-projects": {
"title": "Example Projects"
},
- "functional-component": {
- "title": "Functional Component"
- },
- "gentype": {
- "title": "Gentype & Typescript"
- },
"graphql-apollo": {
"title": "GraphQL & Apollo"
},
@@ -77,12 +59,6 @@
"installation": {
"title": "Installation"
},
- "instance-variables": {
- "title": "Instance Variables"
- },
- "interop": {
- "title": "Talk to Existing ReactJS Code"
- },
"intro-example": {
"title": "Intro Example"
},
@@ -92,66 +68,33 @@
"js-using-reason": {
"title": "ReactJS using ReasonReact"
},
- "jsx-2": {
- "title": "JSX (Old, Version 2)"
- },
"jsx": {
"title": "JSX"
},
- "lifecycles": {
- "title": "Lifecycles"
- },
"playground": {
"title": "Playground"
},
"props-spread": {
"title": "Props Spread"
},
- "react-ref": {
- "title": "React Ref"
- },
"reason-using-js": {
"title": "ReasonReact using ReactJS"
},
- "reasonreactcompat": {
- "title": "ReasonReactCompat: migrating to 0.7.0 and JSX v3"
- },
- "record-field-send-handle-not-found": {
- "title": "Record Field send/handle Not Found"
- },
"refs": {
"title": "Refs in React"
},
"render-props": {
"title": "Render Props"
},
- "render": {
- "title": "Render"
- },
- "roadmap": {
- "title": "Roadmap & Contribution"
- },
- "router-2": {
- "title": "Router"
- },
"router": {
"title": "Router"
},
- "send-handle-callbacks-having-incompatible-types": {
- "title": "send/handle callbacks having Incompatible Types"
- },
"simple": {
"title": "A List of Simple Examples"
},
- "state-actions-reducer": {
- "title": "State, Actions & Reducer"
- },
"style": {
"title": "Style"
},
- "subscriptions-helper": {
- "title": "Subscriptions Helper"
- },
"tailwind-css": {
"title": "Styling: Tailwind CSS"
},
@@ -165,19 +108,19 @@
"title": "useState, useEffect in a Form"
},
"usedebounce-custom-hook": {
- "title": "A Custom useDebounce Hook"
+ "title": "Custom Hooks"
},
"useeffect-hook": {
- "title": "useEffect Hook"
+ "title": "useEffect"
},
"usereducer-hook": {
- "title": "useReducer Hook"
+ "title": "useReducer"
},
"usestate-event-value": {
"title": "Using Event Values With useState"
},
"usestate-hook": {
- "title": "useState Hook"
+ "title": "useState"
},
"what-and-why": {
"title": "What & Why"
@@ -197,6 +140,7 @@
"categories": {
"Getting Started": "Getting Started",
"Core": "Core",
+ "Hooks": "Hooks",
"ReactJS Idioms Equivalents": "ReactJS Idioms Equivalents",
"FAQ": "FAQ",
"Recipes & Snippets": "Recipes & Snippets",
diff --git a/website/sidebars.json b/website/sidebars.json
index 5b6c6ca45..39167ff36 100644
--- a/website/sidebars.json
+++ b/website/sidebars.json
@@ -15,14 +15,19 @@
"refs",
"testing"
],
+ "Hooks": [
+ "usestate-hook",
+ "usereducer-hook",
+ "useeffect-hook",
+ "custom-hooks"
+ ],
"ReactJS Idioms Equivalents": [
"invalid-prop-name",
"props-spread",
"component-as-prop",
"ternary-shortcut",
- "context-mixins",
+ "context",
"custom-class-component-property",
- "usestate-event-value",
"error-boundaries"
],
"FAQ": [
@@ -30,18 +35,12 @@
"record-field-send-handle-not-found",
"send-handle-callbacks-having-incompatible-types",
"i-really-need-feature-x-from-reactjs",
- "element-type-is-invalid",
"i-want-to-create-a-dom-element-without-jsx"
]
},
"examples": {
"Recipes & Snippets": [
"simple",
- "usestate-hook",
- "usereducer-hook",
- "useeffect-hook",
- "use-state-use-effect",
- "usedebounce-custom-hook",
"adding-data-props",
"working-with-optional-data",
"render-props",
diff --git a/website/siteConfig.js b/website/siteConfig.js
index 4ff84e339..b7a910a03 100644
--- a/website/siteConfig.js
+++ b/website/siteConfig.js
@@ -1,169 +1,169 @@
const users = [
- {
- caption: "Facebook",
- image: "img/logos/facebook.svg",
- infoLink: "https://www.facebook.com",
- pinned: true,
- },
- {
- caption: "Messenger",
- image: "img/logos/messenger.svg",
- infoLink: "https://messenger.com",
- pinned: true,
- },
- {
- caption: "BeOp",
- image: "img/logos/beop.svg",
- infoLink: "https://beop.io",
- pinned: true,
- },
- {
- caption: "Social Tables",
- image: "img/logos/socialtables.svg",
- infoLink: "https://www.socialtables.com",
- pinned: true,
- },
- {
- caption: "Ahrefs",
- image: "img/logos/ahrefs.svg",
- infoLink: "https://ahrefs.com",
- pinned: true,
- },
- {
- caption: "Astrocoders",
- image: "img/logos/astrocoders.svg",
- infoLink: "https://astrocoders.com",
- pinned: true,
- },
- {
- caption: "Appier",
- image: "img/logos/appier.svg",
- infoLink: "https://appier.com",
- pinned: true,
- },
- {
- caption: "Imandra Inc.",
- image: "img/logos/imandra.svg",
- infoLink: "https://www.imandra.ai",
- pinned: true,
- },
- {
- caption: "Literal",
- image: "img/logos/literal.svg",
- infoLink: "https://literal.io",
- pinned: true,
- },
- {
- caption: "Online Teaching Platform & LMS | PupilFirst",
- image: "img/logos/pupilfirst.svg",
- infoLink: "https://pupilfirst.com",
- pinned: true,
- },
- {
- caption: "Atvero DMS",
- image: "img/logos/atvero.svg",
- infoLink: "https://www.atvero.com",
- pinned: true,
- },
- {
- caption: "codeheroes",
- image: "img/logos/codeheroes.svg",
- infoLink: "https://codeheroes.io/",
- },
- {
- caption: "Astrolabe Diagnostics",
- image: "img/logos/astrolabe.svg",
- infoLink: "https://astrolabediagnostics.com/",
- },
- {
- caption: "Auditless",
- image: "img/logos/auditless.svg",
- infoLink: "https://auditless.com/",
- pinned: true,
- },
- {
- caption: "Porter",
- image: "img/logos/porter.svg",
- infoLink: "https://porter.in/",
- pinned: true,
- },
- {
- caption: "Lukin Co.",
- image: "img/logos/lukin.svg",
- infoLink: "https://lukin.co",
- pinned: true,
- },
- {
- caption: "Greenlabs",
- image: "img/logos/greenlabs.svg",
- infoLink: "https://greenlabs.co.kr",
- pinned: true,
- },
+ {
+ caption: "Facebook",
+ image: "img/logos/facebook.svg",
+ infoLink: "https://www.facebook.com",
+ pinned: true,
+ },
+ {
+ caption: "Messenger",
+ image: "img/logos/messenger.svg",
+ infoLink: "https://messenger.com",
+ pinned: true,
+ },
+ {
+ caption: "BeOp",
+ image: "img/logos/beop.svg",
+ infoLink: "https://beop.io",
+ pinned: true,
+ },
+ {
+ caption: "Social Tables",
+ image: "img/logos/socialtables.svg",
+ infoLink: "https://www.socialtables.com",
+ pinned: true,
+ },
+ {
+ caption: "Ahrefs",
+ image: "img/logos/ahrefs.svg",
+ infoLink: "https://ahrefs.com",
+ pinned: true,
+ },
+ {
+ caption: "Astrocoders",
+ image: "img/logos/astrocoders.svg",
+ infoLink: "https://astrocoders.com",
+ pinned: true,
+ },
+ {
+ caption: "Appier",
+ image: "img/logos/appier.svg",
+ infoLink: "https://appier.com",
+ pinned: true,
+ },
+ {
+ caption: "Imandra Inc.",
+ image: "img/logos/imandra.svg",
+ infoLink: "https://www.imandra.ai",
+ pinned: true,
+ },
+ {
+ caption: "Literal",
+ image: "img/logos/literal.svg",
+ infoLink: "https://literal.io",
+ pinned: true,
+ },
+ {
+ caption: "Online Teaching Platform & LMS | PupilFirst",
+ image: "img/logos/pupilfirst.svg",
+ infoLink: "https://pupilfirst.com",
+ pinned: true,
+ },
+ {
+ caption: "Atvero DMS",
+ image: "img/logos/atvero.svg",
+ infoLink: "https://www.atvero.com",
+ pinned: true,
+ },
+ {
+ caption: "codeheroes",
+ image: "img/logos/codeheroes.svg",
+ infoLink: "https://codeheroes.io/",
+ },
+ {
+ caption: "Astrolabe Diagnostics",
+ image: "img/logos/astrolabe.svg",
+ infoLink: "https://astrolabediagnostics.com/",
+ },
+ {
+ caption: "Auditless",
+ image: "img/logos/auditless.svg",
+ infoLink: "https://auditless.com/",
+ pinned: true,
+ },
+ {
+ caption: "Porter",
+ image: "img/logos/porter.svg",
+ infoLink: "https://porter.in/",
+ pinned: true,
+ },
+ {
+ caption: "Lukin Co.",
+ image: "img/logos/lukin.svg",
+ infoLink: "https://lukin.co",
+ pinned: true,
+ },
+ {
+ caption: "Greenlabs",
+ image: "img/logos/greenlabs.svg",
+ infoLink: "https://greenlabs.co.kr",
+ pinned: true,
+ },
];
const examples = [
- {
- name: "Hacker News",
- image: "img/examples/hn.png",
- link: "https://github.com/reasonml-community/reason-react-hacker-news",
- },
- {
- name: "TodoMVC",
- image: "img/examples/todomvc.png",
- link: "https://github.com/reasonml-community/reason-react-example/tree/master/src/todomvc",
- },
+ {
+ name: "Hacker News",
+ image: "img/examples/hn.png",
+ link: "https://github.com/reasonml-community/reason-react-hacker-news",
+ },
+ {
+ name: "TodoMVC",
+ image: "img/examples/todomvc.png",
+ link: "https://github.com/reasonml-community/reason-react-example/tree/master/src/todomvc",
+ },
];
-let reasonHighlightJs = require("reason-highlightjs");
+const reasonHighlightJs = require("reason-highlightjs");
const siteConfig = {
- title: "ReasonReact", // title for your website
- tagline: "All your ReactJS knowledge, codified.",
- organizationName: "reasonml",
- url: "https://reasonml.github.io/reason-react" /* your github url */,
- editUrl: "https://github.com/reasonml/reason-react/tree/main/docs/",
- translationRecruitingLink: "https://crowdin.com/project/reason-react",
- baseUrl: "/reason-react/" /* base url for your project */,
- projectName: "reason-react",
- headerLinks: [
- { doc: "installation", label: "Docs" },
- { doc: "playground", label: "Try" },
- { doc: "simple", label: "Examples" },
- { doc: "community", label: "Community" },
- { blog: true, label: "Blog" },
- { languages: true },
- { search: true },
- { href: "https://github.com/reasonml/reason-react", label: "GitHub" },
- ],
- users,
- examples,
- onPageNav: "separate",
- scripts: ["/reason-react/js/pjax-api.min.js"],
- /* path to images for header/footer */
- headerIcon: "img/reason-react-white.svg",
- footerIcon: "img/reason-react-white.svg",
- favicon: "img/reason-react-favicon.png",
- /* colors for website */
- colors: {
- primaryColor: "#48a9dc",
- // darkened 10%
- secondaryColor: "#2F90C3",
- },
- // no .html suffix needed
- cleanUrl: true,
- highlight: {
- theme: "atom-one-light",
- hljs: function (hljs) {
- hljs.registerLanguage("reason", reasonHighlightJs);
- },
- },
- algolia: {
- apiKey: "55156da6520de795d3a2c2d23786f08e",
- indexName: "react-reason",
- algoliaOptions: {
- facetFilters: ["lang:LANGUAGE"],
- },
- },
+ title: "ReasonReact", // title for your website
+ tagline: "All your ReactJS knowledge, codified.",
+ organizationName: "reasonml",
+ url: "https://reasonml.github.io/reason-react" /* your github url */,
+ editUrl: "https://github.com/reasonml/reason-react/tree/main/docs/",
+ translationRecruitingLink: "https://crowdin.com/project/reason-react",
+ baseUrl: "/reason-react/" /* base url for your project */,
+ projectName: "reason-react",
+ headerLinks: [
+ { doc: "installation", label: "Docs" },
+ { doc: "playground", label: "Try" },
+ { doc: "simple", label: "Examples" },
+ { doc: "community", label: "Community" },
+ { blog: true, label: "Blog" },
+ { languages: true },
+ { search: true },
+ { href: "https://github.com/reasonml/reason-react", label: "GitHub" },
+ ],
+ users,
+ examples,
+ onPageNav: "separate",
+ scripts: ["/reason-react/js/pjax-api.min.js"],
+ /* path to images for header/footer */
+ headerIcon: "img/reason-react-white.svg",
+ footerIcon: "img/reason-react-white.svg",
+ favicon: "img/reason-react-favicon.png",
+ /* colors for website */
+ colors: {
+ primaryColor: "#48a9dc",
+ // darkened 10%
+ secondaryColor: "#2F90C3",
+ },
+ // no .html suffix needed
+ cleanUrl: true,
+ highlight: {
+ theme: "atom-one-light",
+ hljs: function (hljs) {
+ hljs.registerLanguage("reason", reasonHighlightJs);
+ },
+ },
+ algolia: {
+ apiKey: "55156da6520de795d3a2c2d23786f08e",
+ indexName: "react-reason",
+ algoliaOptions: {
+ facetFilters: ["lang:LANGUAGE"],
+ },
+ },
};
module.exports = siteConfig;