前々回にlit-html、前回はLitElementについて勉強してきて、それらを使ったWebアプリのサンプルがReduxを使ってたのでコネクタとなるpwa-helpersとともに勉強してみます。
Reduxとは
今さらですが、流行りの状態(state)管理ライブラリ。
そもそもなぜ使う必要があるのかというと、例えばドキュメントツリー関係なく複数の表示更新をしたい時に、イベントメッセージによる親子間のやりとりでは対処しずらいので、state
という全体で共通の状態保存先をつくってやりとりしましょう、と。
仕組みとしては、
-
state
を参照、更新するにはstore
経由に限り、store
は全体で一つしかつくらないこと -
state
を参照するにはstore.getSate()
を使う。 -
state
を更新するにはstore.dispatch()
を使い、action
形式のオブジェクトを渡す -
action
形式とは最低限何をしたいかの識別文字列としてtype
プロパティを持たせる -
action
のtype
プロパティに応じてstate
を更新するreducer
という純関数的処理をつくる
うーん、全然頭に入ってきません。。。 でしたが、ようやくこちらの記事を読み、
-
store
とは申請受付窓口である- 窓口の一本化が図れる
-
action
とは申請書(様式)である-
store.dispatch({type: 'TEST', data: 1})
= 窓口に提出(申請書名,申請内容) - action一覧がどんな申請ができるかの一覧表となって全体像の把握がしやすい
-
-
reducer
とは実際の申請処理である- 同じ申請に対しては忖度されない同じ処理をしてくれるのでテストしやすくなる、はず
と理解しました。
LitElement + Reduxでカウンター
pwa-helpersのconnect
を使うとstateの更新時にstateChanged(state)
が呼ばれるので、そこでLitElementのプロパティにセットします。
※ 非同期処理ライブラリの
redux-thunk
もいれてみてます
import { LitElement, customElement, property, html } from '@polymer/lit-element'
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { connect } from 'pwa-helpers'
// actions
const COUNT_UP = 'count-up'
const countUp = { type: COUNT_UP }
const countUpLate = dispatch => setTimeout(() => dispatch(countUp), 500) // thunk
// reducer
const initState = { counter: 0 }
const reducer = (state = initState, action) => {
switch (action.type) {
case COUNT_UP:
return { ...state, counter: ++state.counter }
default:
return state
}
}
// store
const store = createStore(reducer, applyMiddleware(thunk))
@customElement('my-element') class extends connect(store)(LitElement) {
@property() counter
stateChanged(state) {
this.counter = state.counter
}
render() {
return html`
<h2>counter: ${this.counter}</h2>
<button @click=${() => store.dispatch(countUp)}>+</button>
<button @click=${() => store.dispatch(countUpLate)}>...+</button>
`
}
}
うーん、仕組みはわかってきたものの、なにか遠回りしてる感が。。。
コンポーネント間で値のやりとり
表示とボタンを別エレメントにしてみます。
<counter-view></counter-view>
<counter-button></counter-button>
actionやreducerをつくるのはredux-actions
で楽できそうなので、
import { LitElement, customElement, property, html } from '@polymer/lit-element'
import { createStore } from 'redux'
import { createAction, handleAction } from 'redux-actions'
import { connect } from 'pwa-helpers'
const countUp = createAction('COUNT_UP') // action
const countUpper = handleAction(countUp, state => ({ ...state, counter: ++state.counter }), { counter: 0 }) // reducer
const store = createStore(countUpper) // store
@customElement('counter-view') class extends connect(store)(LitElement) {
@property() counter
stateChanged({ counter }) {
this.counter = counter
}
render() { return html`<h2>counter: ${this.counter}</h2>` }
}
@customElement('counter-button') class extends connect(store)(LitElement) {
render() { return html`<button @click=${() => store.dispatch(countUp())}>+</button>` }
}
すっきりしつつ、ちゃんと受け渡しできてますね。思ってたより使えそうな気がしてきました。
以上。