読者です 読者をやめる 読者になる 読者になる

Write and Run

it's a simple way, but the only way.

XAMLに思いを馳せすぎてReactにDataTemplateを輸入した

WPF、もとい MVVM 信者としては、どうしても pure な Class で ViewModel を作りたいというのがあって*1、React にその思想を持ち込んでみた。

React では往々にして Container Component といわれる MVC の Controller みたいなものと Presentation Component と言われる View みたいなものを多層に重ねて UI を作る。V と C が交互に折り重なるように見えるので、ミルフィーユ方式と呼びたい。

それに対して、MVVM(って言っていいのかな)では、ViewModel という名のただのインスタンスのオブジェクトグラフが1本あって、それとは別に View がある。で、この View は ViewModel のオブジェクトグラフとトラバーサルして変換するような形で作られる。XAML がわからない人は XSLT を想像してください。ああいう感じです(ちょっと違うけど)。

ミルフィーユ方式ではどうしても状態管理と VDOM 生成の責務がべったりしがち、ということがあってあまり好きではないんです。Controller が View を知ってる、みたいになってしまって気持ちよくない。それに対して MVVM の View はロジック(ViewModel)に対してとことん非破壊的なんですね。これがよい。ViewModel は好きなように書いて、View がそれに合わせる。理想じゃないですか。

で、VDOM のポテンシャルを活かせばそれくらいできるでしょう、と午前2時くらいの脳がひらめいたのでちょろっと書いてみました。

まず、なんの変哲もない木構造のクラスを定義します。

export abstract class Node {
  constructor(public value: string) {}
}

export class Tree extends Node {
  constructor(value: string, public children: Node[]) {
    super(value);
  }
}

export class Leaf extends Node {
}

こう、pure な感じで普通に書くわけですね。

で、それをレンダリングする View を書きます。

class TreeView extends ScopedComponent<{
  root: Node
}, {}> {
  render() {
    const {root} = this.props;

    return (
      <ul>
        <DataTemplate type={Tree}>
          {
            ({content}: { content: Tree }) =>
              <li>
                {content.value}
                <ul>
                  {
                    content.children.map((node, i) => {
                      return <ContentControl key={i} content={node} />;
                    })
                  }
                </ul>
              </li>
          }
        </DataTemplate>
        <DataTemplate type={Leaf}>
          {
            ({content}: { content: Leaf }) =>
              <li>
                {content.value}
              </li>
          }
        </DataTemplate>
        <ContentControl content={root} />
      </ul>
    );
  }
}

ナイス非破壊。これだけで再帰構造をレンダリングできます。

実装はこちらにおいてあります。XAML に恋い焦がれている方はどうぞ。PoC なので、スコープが hierarchical cascade じゃないとかで全然実用的ではないですが。

github.com

*1:C#WPF でも DependencyProperty まみれで Pure とはいえないが