Hyperapp試すためにTodoアプリ作った

年末年始あたりにHyperapp関連の記事を沢山見かけたので試してみました

![image.png](https://qiita-image-store.s3.amazonaws.com/0/53219/c2b3c0e5-437d-569f-1f23-acf681b4fa1d.png)

使ったのは[Hyperapp](https://github.com/hyperapp/hyperapp),バンドルには[parcel](https://github.com/parcel-bundler), ホスティングには[Netlify](https://www.netlify.com/)を使った

これが[成果物](https://gracious-dubinsky-fd5d5e.netlify.com/)

##Hyperapp
state, action, view だけのシンプルな感じはすぐに馴染めて楽しかった。
いきなりコード見せると(プログラミング苦手なのでレビューお願いします、もっといい方法とか🙇)

```js:index.js
import { h, app } from 'hyperapp'
import picostyle from 'picostyle'

const ps = picostyle(h)

const STORAGE_KEY = 'MyNameIsBond'
const WEEK = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
const MONTH = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']

const fetchTodos = () => {
  return JSON.parse(window.localStorage.getItem(STORAGE_KEY)) || []
}

const today = new Date()
const dayName = () => {
  return WEEK[today.getDay()]
}

const todayDate = () => {
  return `${MONTH[today.getMonth()]} ${today.getDate()}, ${today.getFullYear()}`
}

const state = {
  todoValue: '',
  todos: fetchTodos(),
  dayName: dayName(),
  todayDate: todayDate()
}

const actions = {
  onInput: value => state => {
    state.todoValue = value
  },

  addTodo: () => state => {
    if (!state.todoValue.length) return
    state.todos.push({
      id: state.todos.length,
      value: state.todoValue,
      completed: false
    })
    window.localStorage.setItem(STORAGE_KEY, JSON.stringify(state.todos))
    state.todoValue = ''
    return state.todos
  },

  removeTodo: id => state => {
    state.todos = state.todos.filter(todo => todo.id !== id)
    window.localStorage.setItem(STORAGE_KEY, JSON.stringify(state.todos))
    return state.todos
  },

  handleCheckbox: index => state => {
    state.todos[index].completed = !state.todos[index].completed
    window.localStorage.setItem(STORAGE_KEY, JSON.stringify(state.todos))
    return state.todos
  },
}

const view = (state, actions) => {
  const Wrapper = ps('main')({
    height: '100%',
    display: 'flex',
    flexDirection: 'column',
    justifyContent: 'center',
    alignItems: 'center',
    textAlign: 'center',
  })

  ~~~ css 省略 ~~~

  return(
    <Wrapper>
      <Content>
        <Week>{ state.dayName }</Week>
        <Date>{ state.todayDate }</Date>
        <TodoLists>
        {
          state.todos.map((todo, index) => {
            return(
                <TodoListsItem
                class={todo.completed ? "completed" : ""}
                >
                <span class="todo-value">{todo.value}</span>

                  <Checkbox
                    type="checkbox"
                    checked={todo.completed}
                    onclick={() => actions.handleCheckbox(index)}
                  ></Checkbox>

                {/* <span
                    class="delete-button"
                    onclick={() => actions.removeTodo(todo.id)}
                  >×</span>
                */}
                </TodoListsItem>
            )
          })
        }
        </TodoLists>
        <TodoInput
          type="text"
          placeholder="What needs to be done?"
          value={state.todoValue}
          oninput={e => actions.onInput(e.target.value)}
          onkeydown={e => e.keyCode === 13 ? actions.addTodo() : ''}
          oncreate={e => e.focus()}
        />
      </Content>
    </Wrapper>
  )
}

export const main = app(state, actions, view, document.body)

```

シンプルなアプリなので説明する所はあまりないかも
Todo removeのコードはあるけど、UIが定まらなくてコメントアウトしちゃった
個人的にjsx書くの初めてだったので、色々勉強になった。(気になったのはみんなjsxの中にロジックとかガンガン書いてるんだけど、jsxでは普通？)
やっぱりもっとJS勉強して上手くならなきゃ...😰

##Parcel, Netlify, おまけ
### Parcel
![image.png](https://qiita-image-store.s3.amazonaws.com/0/53219/d6f354d7-c982-3600-3da3-50c736d2bfc4.png)

ParceとHyperappの連携は[こちら](https://qiita.com/kwst/items/f3460dcaa3a2dcb34b01)の記事を参考にさせてもらった
ほんとにお手軽にバンドルできて感動した🕺

ひとつだけ詰まったのはProduction用のbuild
`parcel build index.html` だとデフォルトの `dist` ディレクトリにバンドルされてJS, CSSのパスが `/dist/foobar.js` になる。その状態でデプロイしちゃってそんなのねーよ！って怒られた。
Production buildは `parcel build index.html --public-url ./` が正解みたい

### Netlify
![image.png](https://qiita-image-store.s3.amazonaws.com/0/53219/257fe6f6-4cb0-036c-f971-a3b4ec922934.png)

めっちゃ簡単、死ぬほど簡単
全部ブラウザで完結するのでHerokuより簡単だと思う

なかでもPrerenderをやってくれる機能があったので使いたかったけど有料プランからだったので諦めた、お金持ちになったら払いたい

### おまけ(picostyle)
スタイリングには[picostyle](https://github.com/picostyle/picostyle)を使った
[こちら](https://qiita.com/morishitter/items/df74dea8d4cecfcf6dec)の記事を読んでHyperapp使うならこれなんだなって勝手に解釈した

今までVueのsingle file compenentしか使ってなかったのでCSS in JSは新鮮だった。
特に悩むことはなかったけど、CSS in JS特有の普通のCSSの書き方から逸脱するのは結構辛かった
SketchかZeplinのデータそのままコピペが不可能でしんどいなーって感想
ex) `marginRight: '16px' => margin-right: 16px;`
[styled-components](https://github.com/styled-com
ponents/styled-components)はそこら辺普通のシンタックスでいけるのでpicostyleも対応してくれるとありがたいなぁ🤔

## 感想
自分みたいなレベルめた低いやろうでも理解はできました
なのでJavaScriptを学んでなんか簡単なアプリケーション作りたい！っていう人におすすめな気がします！
Vueとか色んな所で簡単！って言われるけどなんだかんだVue特有の何かみたいなのは存在するのでやっぱりハードルは高い気が前々からしてました。

Hyperappはその特有さがほぼ0。
かなりとっつき易いです。

このTODOアプリもちょっとずつ更新していこうと思います(アイテム消したり、コンプリートしたものだけのソートとか、リマインダー機能とか)