LoginSignup
0
0

More than 3 years have passed since last update.

vue hackernews

Posted at

vueを使ってhackernewsを表示するチュートリアルを以前やったことがあったので
それをまとめます。

src/store.js

import Vue from 'vue';
import Vuex from 'vuex';

import types from './types';

Vue.use(Vuex);

const BASE_URL = 'https://api.hackernews.io';

export default new Vuex.Store({
  state: {
    newsItems: [],
    currentNewsItem: {},
    loading: false,
  },
  mutations: {
    [types.SET_NEWS_ITEMS](state, newsItems) {
      state.newsItems = newsItems;
    },
    [types.SET_CURRENT_NEWS_ITEM](state, newsItem) {
      state.currentNewsItem = newsItem;
    },
    [types.SET_LOADING](state, loading) {
      state.loading = loading;
    },
    [types.APPEND_NEWS_ITEMS](state, newsItems) {
      const uniqueIds = {};
      state.newsItems = state.newsItems.concat(newsItems).filter((item) => {
        if (!uniqueIds[item.id]) {
          uniqueIds[item.id] = true;
          return true;
        }
        return false;
      });
    },
  },
  actions: {
    async [types.GET_NEWS_ITEMS]({ commit }, { type, page = 1 }) {
      commit(types.SET_LOADING, true);
      if (page === 1) {
        commit(types.SET_NEWS_ITEMS, []);
      }
      const response = await fetch(`${BASE_URL}/${type}?page=${page}`);
      const items = await response.json();
      setTimeout(() => {
        if (page === 1) {
          commit(types.SET_NEWS_ITEMS, items);
        } else {
          commit(types.APPEND_NEWS_ITEMS, items);
        }
        commit(types.SET_LOADING, false);
      }, 1000);
    },
    async [types.GET_NEWS_ITEM]({ commit }, id) {
      commit(types.SET_LOADING, true);
      const response = await fetch(`${BASE_URL}/item/${id}`);

      const item = await response.json();
      setTimeout(() => {
        commit(types.SET_CURRENT_NEWS_ITEM, item);
        commit(types.SET_LOADING, false);
      }, 1000);
    },
  },
});

mutations、actionsについてまとめてあるので解説します。
mutations

実際に Vuex のストアの状態を変更できる唯一の方法は、ミューテーションをコミットすることです。Vuex のミューテーションはイベントにとても近い概念です: 各ミューテーションはタイプとハンドラを持ちます。ハンドラ関数は Vuex の状態(state)を第1引数として取得し、実際に状態の
変更を行います

actionsについて 

アクションはミューテーションと似ていますが、下記の点で異なります:

アクションは、状態を変更するのではなく、ミューテーションをコミットします。
アクションは任意の非同期処理を含むことができます。

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 状態を変更する
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

非同期でのactionsの例

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

ショッピングカートをチェックアウトするアクション

actions: {
  checkout ({ commit, state }, products) {
    // 現在のカート内の商品を保存する
    const savedCartItems = [...state.cart.added]
    // チェックアウトのリクエストを送信し、楽観的にカート内をクリアする
    commit(types.CHECKOUT_REQUEST)
    // shop API は成功時のコールバックと失敗時のコールバックを受け取る
    shop.buyProducts(
      products,
      // 成功時の処理
      () => commit(types.CHECKOUT_SUCCESS),
      // 失敗時の処理
      () => commit(types.CHECKOUT_FAILURE, savedCartItems)
    )
  }
}

src/views/Home.vue

<template>
  <div class="home">
    <div>
      <news-item v-for="item in newsItems" :key="item.id" :item="item" />
    </div>
    <div v-if="!loading">
      <p class="more" @click="loadMore">More</p>
    </div>
    <div v-if="loading">
      <h3>Loading...</h3>
    </div>
  </div>
</template>

<script>
import { value, watch, onCreated } from 'vue-function-api';
import { useState, useActions, useRouter } from '@u3u/vue-hooks';
import types from '../types';
import NewsItem from '../components/NewsItem.vue';
export default {
  components: {
    NewsItem,
  },
  setup() {
    const { route } = useRouter();
    const { loading, newsItems } = useState(['loading', 'newsItems']);
    const { GET_NEWS_ITEMS } = useActions([types.GET_NEWS_ITEMS]);
    const currentPage = value(1);
    const setCurrentType = (type) => {
      currentPage.value = 1;
      GET_NEWS_ITEMS({
        type,
        page: currentPage.value,
      });
    };
    watch(() => route.value.params.type, (type) => {
      setCurrentType(type);
    });
    onCreated(() => {
      setCurrentType(route.value.params.type);
    });
    const loadMore = () => {
      currentPage.value += 1;
      GET_NEWS_ITEMS({
        type: route.value.params.type,
        page: currentPage.value,
      });
    };
    return {
      loading,
      newsItems,
      loadMore,
    };
  },
};
</script>
0
0
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
0
0