14
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Reason 気になった人はまず最初で使うであろう ReasonReact について解説する。

外部パッケージの使い方

npm経由で落として、bsconfig.json にバインディングがあるので、それを教えてやる。

yarn add reason-react
bsconfig.json
{
  ...
  "bs-dependencies": ["reason-react"]
}

これで bucklescript が ReasonReact 名前空間にアクセスできる。

Hello, ReasonReact

コンポーネントを定義して render する。

main.re
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 を定義することも出来る。

なので、別ファイルに切り出すならこうなる

app.re
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 を呼ぶ

まだ調べてない。わかったら書く。

14
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?