Permalink
Browse files

New syntax

  • Loading branch information...
chenglou committed Oct 27, 2017
1 parent d286198 commit 3640ce6bb6729c3b3d8a0c200a624996f8da8beb
@@ -14,4 +14,5 @@
// "module": "commonjs",
// "in-source": true,
// },
"refmt": 3
}
@@ -15,6 +15,15 @@ function make() {
})), 1000)];
return /* NoUpdate */0;
});
newrecord[/* willUnmount */6] = (function (param) {
var match = param[/* state */4][/* timerId */1][0];
if (match) {
clearInterval(match[0]);
return /* () */0;
} else {
return /* () */0;
}
});
newrecord[/* render */9] = (function (param) {
var count = param[/* state */4][/* count */0];
var match = +(count === 1);
@@ -18,12 +18,12 @@
"bs-director": "^0.0.4",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"reason-react": "^0.2.5",
"reason-react": "^0.2.4",
"todomvc-app-css": "^2.0.0",
"todomvc-common": "^1.0.1"
},
"devDependencies": {
"bs-platform": "^1.9.2",
"bs-platform": "^2.0.0",
"concurrently": "^3.5.0",
"webpack": "^1.14.0"
}
@@ -1,38 +1,36 @@
/* This is a stateful component. In ReasonReact, we call them reducer components */

/* A list of state transitions, to be used in self.reduce and reducer */
type action =
| Tick;

/* The component's state type. It can be anything, including, commonly, being a record type */
type state = {
count: int,
timerId: ref (option Js.Global.intervalId)
timerId: ref(option(Js.Global.intervalId))

This comment has been minimized.

@vjeux

vjeux Oct 28, 2017

Too bad it's not using <> :(

};

let component = ReasonReact.reducerComponent "Counter";
let component = ReasonReact.reducerComponent("Counter");

let make _children => {
let make = (_children) => {
...component,
initialState: fun () => {count: 0, timerId: ref None},
reducer: fun action state =>
initialState: () => {count: 0, timerId: ref(None)},
reducer: (action, state) =>

This comment has been minimized.

@mkonicek

mkonicek Oct 29, 2017

Like this much better!

This comment has been minimized.

@fikriauliya

fikriauliya Nov 12, 2017

Wow, it looks much more like JS now.

switch action {
| Tick => ReasonReact.Update {...state, count: state.count + 1}
| Tick => ReasonReact.Update({...state, count: state.count + 1})
},
didMount: fun self => {
didMount: (self) => {
/* this will call `reduce` every second */
self.state.timerId := Some (Js.Global.setInterval (self.reduce (fun _ => Tick)) 1000);
self.state.timerId := Some(Js.Global.setInterval(self.reduce((_) => Tick), 1000));
ReasonReact.NoUpdate
},
willUnmount: fun {state} => {
switch !state.timerId {
| Some id => Js.Global.clearInterval id
| _ => ()
}
},
render: fun {state: {count}} => {
willUnmount: ({state}) =>
switch state.timerId^ {
| Some(id) => Js.Global.clearInterval(id)
| _ => ()
},
render: ({state: {count}}) => {
let timesMessage = count == 1 ? "second" : "seconds";
let greeting = {j|You've spent $count $timesMessage on this page!|j};
<div> (ReasonReact.stringToElement greeting) </div>
<div> (ReasonReact.stringToElement(greeting)) </div>
}
};
@@ -1 +1 @@
ReactDOMRe.renderToElementWithId <Counter /> "index";
ReactDOMRe.renderToElementWithId(<Counter />, "index");
@@ -1,18 +1,17 @@
/* ReasonReact used by ReactJS */

/* This is just a normal stateless component. The only change you need to turn
it into a ReactJS-compatible component is the wrapReasonForJs call below */
let component = ReasonReact.statelessComponent "PageReason";
let component = ReasonReact.statelessComponent("PageReason");

let make ::message ::extraGreeting=? _children => {
let make = (~message, ~extraGreeting=?, _children) => {
...component,
render: fun _self => {
render: (_self) => {
let greeting =
switch extraGreeting {
| None => "How are you?"
| Some g => g
| Some(g) => g
};
<div> <MyBannerRe show=true message=(message ^ " " ^ greeting) /> </div>
<div> <MyBannerRe show=true message=(message ++ (" " ++ greeting)) /> </div>
}
};

@@ -23,12 +22,12 @@ let make ::message ::extraGreeting=? _children => {
the correct babel/webpack setup, you can also do `let default = ...` and use it
on the JS side as a default export. */
let jsComponent =
ReasonReact.wrapReasonForJs
::component
(
fun jsProps =>
make
message::jsProps##message
extraGreeting::?(Js.Null_undefined.to_opt jsProps##extraGreeting)
[||]
);
ReasonReact.wrapReasonForJs(
~component,
(jsProps) =>
make(
~message=jsProps##message,
~extraGreeting=?Js.Null_undefined.to_opt(jsProps##extraGreeting),
[||]
)
);
@@ -1,18 +1,17 @@
/* ReactJS used by ReasonReact */

/* This component wraps a ReactJS one, so that ReasonReact components can consume it */

/* Typing the myBanner.js component's output as a `reactClass`. */
/* Note that this file's JS output is located at reason-react-example/lib/js/src/interop/myBannerRe.js; we're specifying the relative path to myBanner.js in the string below */
/* This isn't ideal; but if you turn on in-source js generation (check this file's bsconfig.json then you'd only need `"./MyBanner"`) */
external myBanner : ReasonReact.reactClass = "../../../../src/interop/MyBanner" [@@bs.module];
[@bs.module] external myBanner : ReasonReact.reactClass = "../../../../src/interop/MyBanner";

/* This is like declaring a normal ReasonReact component's `make` function, except the body is a the interop hook wrapJsForReason */
let make ::show ::message children =>
ReasonReact.wrapJsForReason
reactClass::myBanner
props::{
"show": Js.Boolean.to_js_boolean show, /* ^ don't forget to convert an OCaml bool into a JS boolean! */
let make = (~show, ~message, children) =>
ReasonReact.wrapJsForReason(
~reactClass=myBanner,
~props={
"show": Js.Boolean.to_js_boolean(show), /* ^ don't forget to convert an OCaml bool into a JS boolean! */
"message": message /* OCaml string maps to JS string, no conversion needed here */
}
children;
},
children
);
@@ -1,10 +1,9 @@
/* Here's a big spinning logo. No reason why. It's nice */

external requestAnimationFrame : (unit => unit) => unit = "" [@@bs.val];
[@bs.val] external requestAnimationFrame : (unit => unit) => unit = "";

open Constants;

let renderGraphic rotationStyle =>
let renderGraphic = (rotationStyle) =>
<g fill="none" stroke="none">
<g transform="scale(1.5, 1.5) translate(100.000000, 105.000000)">
<path fill="rgba(0,0,0,0.1)" d=border_path />
@@ -34,59 +33,60 @@ type state = {
lastMs: float
};

let component = ReasonReact.reducerComponent "LogoRe";
let component = ReasonReact.reducerComponent("LogoRe");

let make ::message _children => {
let make = (~message, _children) => {
...component,
initialState: fun () => {drag: mouseUpDrag, degrees: 0.0, velocity: 0.1, lastMs: Js.Date.now ()},
reducer: fun action state =>
initialState: () => {drag: mouseUpDrag, degrees: 0.0, velocity: 0.1, lastMs: Js.Date.now()},
reducer: (action, state) =>
switch action {
| MouseUp =>
let withAccel = state.velocity +. clickAccel;
let nextVelocity = withAccel < maxVel ? withAccel : maxVel;
ReasonReact.Update {...state, velocity: nextVelocity, drag: mouseUpDrag}
| MouseDown => ReasonReact.Update {...state, drag: mouseDownDrag}
ReasonReact.Update({...state, velocity: nextVelocity, drag: mouseUpDrag})
| MouseDown => ReasonReact.Update({...state, drag: mouseDownDrag})
| Spin =>
let now = Js.Date.now ();
let now = Js.Date.now();
/* How many 16ms virtual frames elapsed, even if clock runs at 30hz */
let idealFramesSinceLast = 1. +. (now -. state.lastMs) /. 16.;
let nextDegrees = state.degrees +. (baseVel +. state.velocity) *. idealFramesSinceLast;
let nextVelocity = state.velocity *. state.drag;
ReasonReact.Update {...state, degrees: nextDegrees, velocity: nextVelocity, lastMs: now}
ReasonReact.Update({...state, degrees: nextDegrees, velocity: nextVelocity, lastMs: now})
},
didMount: fun {reduce} => {
let rec onAnimationFrame () => {
reduce (fun _ => Spin) ();
requestAnimationFrame onAnimationFrame
didMount: ({reduce}) => {
let rec onAnimationFrame = () => {
reduce((_) => Spin, ());
requestAnimationFrame(onAnimationFrame)
};
requestAnimationFrame onAnimationFrame;
requestAnimationFrame(onAnimationFrame);
ReasonReact.NoUpdate
},
render: fun {state, reduce} => {
let transform = "rotate(" ^ string_of_float state.degrees ^ "deg)";
render: ({state, reduce}) => {
let transform = "rotate(" ++ (string_of_float(state.degrees) ++ "deg)");
/* One of the ways to create JS Objects in Reason, through BuckleScript's `external`s */
let rotationStyle = ReactDOMRe.Style.make transformOrigin::"50% 50%" ::transform ();
let rotationStyle = ReactDOMRe.Style.make(~transformOrigin="50% 50%", ~transform, ());
<div
style=(
ReactDOMRe.Style.make
color::"#444444"
userSelect::"none"
paddingTop::"40px"
fontSize::"68px"
fontFamily::"Montserrat"
textAlign::"center"
ReactDOMRe.Style.make(
~color="#444444",
~userSelect="none",
~paddingTop="40px",
~fontSize="68px",
~fontFamily="Montserrat",
~textAlign="center",
()
)
)>
(ReasonReact.stringToElement message)
(ReasonReact.stringToElement(message))
<svg
width="100%"
height="100%"
viewBox="0 0 700 700"
version="1.1"
style=(ReactDOMRe.Style.make cursor::"pointer" ())
onMouseUp=(reduce (fun _ => MouseUp))
onMouseDown=(reduce (fun _ => MouseDown))>
(renderGraphic rotationStyle)
style=(ReactDOMRe.Style.make(~cursor="pointer", ()))
onMouseUp=(reduce((_) => MouseUp))
onMouseDown=(reduce((_) => MouseDown))>
(renderGraphic(rotationStyle))
</svg>
</div>
}
@@ -1 +1 @@
ReactDOMRe.renderToElementWithId <Logo message="REASON REACT" /> "index";
ReactDOMRe.renderToElementWithId(<Logo message="REASON REACT" />, "index");
@@ -2,15 +2,15 @@
/* retainedProps allows you to access the previous props information, like how ReactJS does it for you in lifecycle events */
type retainedProps = {message: string};

let component = ReasonReact.statelessComponentWithRetainedProps "RetainedPropsExample";
let component = ReasonReact.statelessComponentWithRetainedProps("RetainedPropsExample");

let make ::message _children => {
let make = (~message, _children) => {
...component,
retainedProps: {message: message},
didUpdate: fun {oldSelf, newSelf} =>
didUpdate: ({oldSelf, newSelf}) =>
if (oldSelf.retainedProps.message !== newSelf.retainedProps.message) {
/* do whatever sneaky imperative things here */
Js.log "props `message` changed!"
Js.log("props `message` changed!")
},
render: fun _self => <div> (ReasonReact.stringToElement message) </div>
render: (_self) => <div> (ReasonReact.stringToElement(message)) </div>
};
@@ -1,12 +1,14 @@
let toggle = ref false;
let toggle = ref(false);

let render () => {
toggle := not toggle.contents;
ReactDOMRe.renderToElementWithId
<RetainedPropsExample message=(toggle.contents ? "Hello!" : "Goodbye") /> "index"
let render = () => {
toggle := ! toggle.contents;
ReactDOMRe.renderToElementWithId(
<RetainedPropsExample message=(toggle.contents ? "Hello!" : "Goodbye") />,
"index"
)
};

Js.Global.setInterval render 1000;
Js.Global.setInterval(render, 1000);

/* render once first! */
render ();
render();
@@ -1,10 +1,10 @@
/* This is the basic component. */
let component = ReasonReact.statelessComponent "Page";
let component = ReasonReact.statelessComponent("Page");

/* Your familiar handleClick from ReactJS. This mandatorily takes the payload,
then the `self` record, which contains state (none here), `handle`, `reduce`
and other utilities */
let handleClick _event _self => Js.log "clicked!";
let handleClick = (_event, _self) => Js.log("clicked!");

/* `make` is the function that mandatorily takes `children` (if you want to use
`JSX). `message` is a named argument, which simulates ReactJS props. Usage:
@@ -14,8 +14,8 @@ let handleClick _event _self => Js.log "clicked!";
Which desugars to
`ReasonReact.element (Page.make message::"hello" [||])` */
let make ::message _children => {
let make = (~message, _children) => {
...component,
render: fun self =>
<div onClick=(self.handle handleClick)> (ReasonReact.stringToElement message) </div>
render: (self) =>
<div onClick=(self.handle(handleClick))> (ReasonReact.stringToElement(message)) </div>
};
@@ -1 +1 @@
ReactDOMRe.renderToElementWithId <Page message="Hello!" /> "index";
ReactDOMRe.renderToElementWithId(<Page message="Hello!" />, "index");
Oops, something went wrong.

0 comments on commit 3640ce6

Please sign in to comment.