Talk to Existing ReactJS Code
Project Setup
You can reuse the same bsb setup (that you might have seen here)! Aka, put a bsconfig.json
at the root of your ReactJS project:
{
"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
):
"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
:
[@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:
[@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 generatedjsProps
function from thejsProps
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.
ReactJS Using ReasonReact
Eeeeasy. We expose a helper for the other direction, ReasonReact.wrapReasonForJs
:
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
andjsProps->ageGet
part. This is a getter generated bybs.deriving abstract
. Documentations here.
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:
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.