mmyoji's diary

プログラミングとか日々のどうでもいいこととか

Vue.js + redux でカレンダー作った

前にカレンダーをクソみたいなコードで作ってましたがそれを少し修正して、且つreduxを導入してみました。

なんとなーく概念を理解してはいたんですが、一人でreduxの構成でコード書けるかって言われると微妙な感じだったので練習がてらやってみました。

mmyoji/vue_calendar at apply-redux · GitHub

とりあえず最低限覚えておけばいいのが以下のこと

redux覚えておきたい関数

  • combineReducers : 作成したreducerを束ねる
  • createStore : reducer(s)を引数に渡してstoreを作る
  • store.dispatch : objectを引数に取ってactionを実行しstateを更新
  • store.subscribe : dispatchが起きたあとに引数のcallbackを実行

箇条書きにされてもなんじゃそらって感じなのでもうちょっと詳しく書きます(一部のみ)

わざと冗長に書いてる部分はあるのでツッコミなしでおねがいしますmm

/* action.js */

export const GO_NEXT_MONTH = "GO_NEXT_MONTH"
export const BACK_LAST_MONTH = "BACK_LAST_MONTH"

// 「actionはただの関数」という説明を受けると思いますが本当にこれだけ
// これは実際にUserから入力を受け取った際に使用します
// この関数は引数forwardに進めたい月数(integer)を入れます
export function goNextMonth(forward) {
  return {
    type: GO_NEXT_MONTH,
    forward
  }
}



/* reducer.js */
import { combineReducers } from "redux"
const today = new Date()
const defaultState = {
  year: today.getFullYear(),
  month: today.getMonth(),
  date: today.getDate(),
}

export current(state = defaultState, action) {
  switch (action.type) {
  case GO_NEXT_MONTH:
    // サンプルでは関数呼び出したりしてますがあまり気にしないでくださいmm
    return {
      year: state.year,
      month: state.month + action.forward,
      date: state.date,
    }
  case BACK_LAST_MONTH:
    return {
      year: state.year,
      month: state.month - action.backward,
      date: state.date,
    }
  default:
    return state
  }
}

// 今は1個しか渡してませんが今後増える可能性あるのでこうしてます
const calendarReducers combineReducers({
  current,
})

export default calendarReducers


/* app.js */
// new Vue() に渡すオブジェクトを書いてます
// Reactで言うところのcomponentと思ってください

import { createStore } from "redux"
import { merge } from "lodash"

import {
  goNextMonth,
  backLastMonth,
} from "./action"
import calendarReducers from "./reducer"

const store = createStore(calendarReducers)
// 初期状態のstate(vueではdata)を取得
const defaultData = store.getState()
//=> { current: Object }

export default {
  el: "#app",
  data: defaultData,
  created() {
    // vue instanceの作成時にstoreの変更を監視
    store.subscribe(() => {
      this.$set("current", store.getState().current)
    })
  },

  computed: {
    headerLabel() {
      return `${this.current.year}/${this.current.month + 1}`
    }
  },

  methods: {
    goNextMonth() {
      // 1ヶ月進める
      store.dispatch(goNextMonth(1))
    },
    // backLastMonth() {
  }
}

色々端折りましたが大体こんな感じで、 store.dispatch されたら subscribe の引数の関数が呼ばれて、this.current が更新され、viewにある headerLabel も更新される、みたいな感じです。

所感

Basics | Redux を今日はしばらく眺めていたんですが、これだと例がReactで書かれていて react-redux なるもののせいで色々省略されていてわかりづらかったんですが、Vue.jsに適用することで理解が深まりました。(主に実装部分)

最初はfluxの時同様「冗長だなぁ」と思ってましたが、component側でごちゃごちゃ書くことがなくなるのでコードをかなり綺麗にまとめられるなーという印象です。めっちゃいいです。

サンプルコードには lib/ とかっていう「何でもおけるじゃねーかバカやろこのやろー」的なディレクトリ置いちゃったんですが、redux採用したなら reducers/, actions/, components/ , stores/ という風に分けて書いた方が圧倒的にいいなと思いました。

仕事では僕の意見だけで採用を決めるのが難しいので(「フロントエンドは流行り廃りが激しいから安易に食いつくな」と言われます)、プライベートで積極的に採用していこうかなー