reason-react-brunch
Version:
React bindings for Reason, modified to work with brunch and bucklescript
151 lines (115 loc) • 4.68 kB
Markdown
---
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 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
[.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` */
[.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
[.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 ...;
[.deriving abstract]
type jsProps = {
name: string,
age: Js.nullable(int),
};
let jsComponent =
ReasonReact.wrapReasonForJs(~component, jsProps =>
make(
~name=jsProps |. name,
~age=?Js.Nullable.toOption(jsProps |. age),
[||],
)
);
```
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).
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!
<MyReasonComponent name="John" />
```
**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).