nuxt-ts day3:型安全なVuexでtodoリストを作る
2020-12-02
# advent

3日目。

https://nuxt-ts-sample.netlify.app/todolist

作った。

compotisionAPI で todoリストを作る

todolist.vue

import { reactive } from "@vue/composition-api";

interface Todo {
  todo: string,
  todos: string[]
}

export default {
  setup() {
    const state = reactive<Todo>({
      todo: '',
      todos: []
    })
    const addTodo = () => {
      state.todos.push(state.todo)
      state.todo = ''
    }
    const removeTodo = (index: number) => state.todos.splice(index,1)

    return {
      state,
      addTodo,
      removeTodo
    }
  }
}

シンプルな形です。

Vuex を導入する

ストア - Nuxt TypeScript

公式のドキュメントに従って進めていきます。そもそもvuexが型推論と相性が悪いようなので、色々と過程を踏む必要があるらしい。

モジュールとして、store の内容を記述します。

store\todo.ts

import { Module, VuexModule, Mutation } from 'vuex-module-decorators'

@Module({
  name: 'todo',
  stateFactory: true,
  namespaced: true
})
export default class Todos extends VuexModule {
  private todos: string[] = ['task1']

  public get getTodos () {
    return this.todos
  }

  @Mutation
  public add (todo: string) {
    this.todos.push(todo)
  }

  @Mutation
  public remove (id: number) {
    this.todos.splice(id, 1)
  }
}

ここでモジュールを store として読み込みます。

store\index.ts

import { Store } from 'vuex'
import { initialiseStores } from '~/utils/store-accessor'
const initializer = (store: Store<any>) => initialiseStores(store)
export const plugins = [initializer]
export * from '~/utils/store-accessor'

アクセサーを作ります。これでcomponentから TodoStore が参照できるようになりました。

新たに store を作りたいときはここに追記していく必要があります。

utils\store-accessor.ts

/* eslint-disable import/no-mutable-exports */
import { Store } from 'vuex'
import { getModule } from 'vuex-module-decorators'
import Todo from '~/store/todo'

let TodoStore: Todo
function initialiseStores (store: Store<any>): void {
  TodoStore = getModule(Todo, store)
}

export { initialiseStores, TodoStore }

todolistコンポーネントを store 仕様に書き換えます。

todolist.vue

import { defineComponent, reactive, computed } from '@vue/composition-api'
import { TodoStore } from '~/store'

interface Todo {
  todo: string
}

export default defineComponent({
  setup () {
    const state = reactive<Todo>({
      todo: ''
    })
    const todos = TodoStore
    const todolist = computed(() => todos.getTodos)

    const addTodo = () => {
      todos.add(state.todo)
      state.todo = ''
    }
    const removeTodo = (index: number) => {
      todos.remove(index)
    }

    return {
      state,
      todolist,
      addTodo,
      removeTodo
    }
  }
})

tailwindcss の使用感

template部はこんな感じに。tailwindcssをやっている。

todolist.vue

<template>
  <div class="justify-center items-center text-center max-w-md mx-auto">
    <h1>Todoリスト</h1>
    <div class="flex">
      <input v-model="state.todo" class="bg-white focus:outline-none focus:shadow-outline border border-gray-300 rounded-lg py-2 px-4 block w-full leading-normal" placeholder="taskを入力してください">
      <button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded w-20 ml-3" @click="addTodo">
        追加
      </button>
    </div>
    <br>
    <li v-for="(todo, index) in todolist" :key="index" class="py-1 text-left">
      {{ todo }}
      <button class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 ml-3 rounded-full" @click="removeTodo(index)">
        delete
      </button>
    </li>
</template>

現段階ではいまいち便利さを図り損ねている。微調整がかんたんなのは嬉しいけど、新たなコンポーネントを作るたびに設定しなきゃなのが面倒ですね。CSSの勉強にはなりそうだ。