Reason 気になった人はまず最初で使うであろう ReasonReact について解説する。
外部パッケージの使い方
npm経由で落として、bsconfig.json にバインディングがあるので、それを教えてやる。
yarn add reason-react
{
...
"bs-dependencies": ["reason-react"]
}
これで bucklescript が ReasonReact 名前空間にアクセスできる。
Hello, ReasonReact
コンポーネントを定義して render する。
module App {
let make = (_children) => {
...ReasonReact.statelessComponent("App"),
render: (self) => <div> { ReasonReact.stringToElement("Hello") } </div>
};
}
/* mount */
ReactDOMRe.renderToElementWithId(<App />, "root");
このコードを見る限り、ReasonReact は make という関数を見つけて React.createElement 相当の展開をする。
文字列は ReasonReact.stringToElement("Hello")
という感じで埋める。多少面倒くさい。
名前空間の話
ここで、初歩っぽい話をするが(自分はocaml詳しくないので、こういうタイミングで新しいことを知る)、 ocamlのファイルスコープはファイル名が PascalCase に変換されて module 名になるが、自分自身で module を定義することも出来る。
なので、別ファイルに切り出すならこうなる
let make = (_children) => {
...ReasonReact.statelessComponent("App"),
render: (self) => <div> { ReasonReact.stringToElement("Hello") } </div>
};
bucklescript 的には外部の名前空間へのアクセスを発見すると require('./src/App')
的な変換をする。
reducerComponent
Reactのアプリを作るなら、 redux 相当のことをしたいよな、と思って、本当はここで reductive という redux クローンの解説をしようと思っていたが、 ドキュメントいわく、 You might not need this library, なぜなら ReasonReact が reducer 相当の機能を持ってるので、まずはそれを使え、という話。
https://github.com/reasonml-community/reductive は後日解説する。(こうやって記事を稼ぐ)
https://reasonml.github.io/reason-react/blog/2017/09/01/reducers.html を読みながらカウンターを実装してみた。
type action =
| Increment
| Decrement;
let counter = (action, state) =>
switch action {
| Increment => state + 1
| Decrement => state - 1
};
module App = {
let make = (~value, ~onClickIncrement, ~onClickDecrement, _children) => {
...ReasonReact.statelessComponent("App"),
render: (self) =>
<div>
<span> (ReasonReact.stringToElement(string_of_int(value))) </span>
<button onClick=onClickIncrement>
(ReasonReact.stringToElement("+1"))
</button>
<button onClick=onClickIncrement>
(ReasonReact.stringToElement("-1"))
</button>
</div>
};
};
let make = (_children) => {
...ReasonReact.reducerComponent("AppContainer"),
initialState: () => 0,
reducer: (action, state) => ReasonReact.Update(counter(action, state)),
render: (self) =>
<div>
<App
value=self.state
onClickIncrement=(self.reduce((_) => Increment))
onClickDecrement=(self.reduce((_) => Decrement))
/>
</div>
};
counter が ピュアな reducer 定義、 App が View を持つComponent、 トップレベルの make がいわゆる ContainerComponent という感じの分担。ちょっとややこしいのが self.reduce が高階関数になっている。
JS でやるときの無理矢理な action 定義と違って、言語組み込みの機能で自然に表現できている。
次の記事の候補
- reductive さっと調べる限り connect 実装してないが、それはどうする?
- Reason から JS で定義した React Component を呼ぶ
- JS から Reason で定義した React Component を呼ぶ
まだ調べてない。わかったら書く。