LoginSignup
2
1

More than 5 years have passed since last update.

Classを使用してHyperappを書いてみた

Last updated at Posted at 2018-06-09

stateactionsview を Class で書いておけば再利用も楽になりそうと思ってやってみた。多分いい感じ。

Hyperapp用のクラスをHyperclassって呼びたい。

hyperclass.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <script type="module">

      import { h, app as mount} from 'https://unpkg.com/hyperapp?module'

      class Hyperclass {
        constructor() {
          this.state = {}
          this.actions = {}
          this.view = (state, actions) => h("h1", {}, "Hyperclass")
        }
        appendTo(container) {
          return mount(this.state, this.actions, this.view, container)
        }
        assign(namespace, component) {
          this[namespace] = { view: component.view }
          this.state[namespace] = component.state
          this.actions[namespace] = component.actions
        }
      }

      class Counter {
        constructor(classname='counter') {
          this.state = {
            count: 0,
          }
          this.actions = {
            up  : (by) => (state, actions) => ({count: state.count+by}),
            down: (by) => (state, actions) => ({count: state.count-by}),
          }
          this.view = (state, actions) => h("div", {class:classname}, [
            h("h1", {}, state.count),
            h("button", {onclick:()=>actions.up(1)}, '+1'),
            h("button", {onclick:()=>actions.down(1)}, '-1'),
          ])
        }
      }

      class TwinCounter extends Hyperclass {
        constructor(cls='twinCounter') {
          super()
          this.assign('c1', new Counter())
          this.assign('c2', new Counter())
          Object.assign(this.actions, {
            twin: _ => (s,a) => {
              a.c1.up(1)
              a.c2.down(1)
            },
          })
          const tree = c => h("div",{class:cls},c)
          const button = t => (s,a) => h("button",{onclick:a.twin},t)
          const view1 = _ => (s,a) => this.c1.view(s.c1,a.c1)
          const view2 = _ => (s,a) => this.c2.view(s.c2,a.c2)
          this.view = (s,a) => tree([
            button("twin action"),
            view1(),
            view2(),
          ])
        }
      }

      const component = new TwinCounter()

      const widget = component.appendTo(document.body)

      for(let msec=0; msec<10000; msec++) setTimeout(widget.twin, msec)

    </script>
  </body>
</html>

追記:次期Hyperappでこんな風に書けるといいな。自分でもチャレンジしてみたい。

hyperclass-example.js
import Hyperclass from './hyperclass.js'

class Counter extends Hyperclass {
  constructor(classname='counter') {
    this.classname = classname
    this.count = 0
  }
  up(by) {
    //this.count += by
    return self => ({count: self.count + by})
  }
  down(by) {
    //this.count -= by
    return self => ({count: self.count - by})
  }
  view(self) {
    return h("div", {class:self.classname}, [
      h("h1", {}, self.count),
      h("button", {onclick:()=>self.up(1)}, '+1'),
      h("button", {onclick:()=>self.down(1)}, '-1'),
    ])
  }
}

class TwinCounter extends Hyperclass {
  constructor(classname='twincounter') {
    this.classname = classname
    this.counter1 = new Counter()
    this.counter2 = new Counter()
  }
  twin() {
    return self => {
      self.counter1.up(1)
      self.counter2.down(1)
    }
  }
  view(self) {
    return h("div", {class:self.classname}, [
      h("button", {onclick:()=>self.twin()}, "twin action"),
      self.counter1.view(self.counter1),
      self.counter1.view(self.counter2),
    ])
  }
}

new TwinCounter().appendTo(document.body)
2
1
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
2
1