Vue.jsとFirestoreでのフォロー機能実装(Vuex)

2021.01.18

見出し画像

今回はフォロー機能について書いていきます!

基本的にはお気に入り機能と同じ流れになっているので、もしかするとそっちの方が見やすいかもです。

https://note.com/tenlife/n/n8956cae544ec

目次

  1. 実装した内容
  2. 実装の流れ
  3. 実際のコード
  4. ポイント
  5. 実装してみて

実装した内容

フォロー(解除も)

フォローリストの作成、取得(フォロワーも)

フォローしているユーザーの投稿一覧、フォローされてますの表示。

実装の流れ

Usersテーブルの作成。

ユーザー詳細ページの作成。フォローボタンの作成。

storeの方で振り分け処理。

actions->mutations->stateの流れで値を代入。

vueページでstateから値を取得。

取得した値を既にフォローしているのか、してないのかでstatusを付与する。

statusの値に応じてフォローするボタンの表示をかえる

実際のコード

<template>
 <div>
   <p>{{detailUser.name}}さん</p>
   <v-btn
     color="green lighten-1"
     class="mx-0"
     outlined
     v-if="!detailUser.follow_status"
     @click.prevent="follow(detailUser)"
     >
     <p>フォローする</p>
   </v-btn>
   <v-btn
     color="green lighten-1"
     class="mx-0"
     outlined
     v-else
     @click.prevent="follow(detailUser)"
     >
     <p>フォロー解除</p>
   </v-btn>
   <p v-if="detailUser.followed_status">フォローされています</p>
   <p>投稿一覧</p>
   <div v-for="post in userPosts" :key="post.id" style="margin-bottom: 20px;">
     <template>
       <v-card
         class="mx-auto"
         max-width="344"
         outlined
       >
         <v-list-item three-line>
           <v-list-item-content>
             <div class="overline mb-4">{{detailUser.name}}さん</div>
             <v-list-item-title class="headline mb-1"><router-link :to="{name: 'detail',params:{ postId: post.id}}" :data="post.user"  style="text-decoration: none; color: black;">{{post.title}}</router-link></v-list-item-title>
             <v-list-item-subtitle>{{post.text}}</v-list-item-subtitle>
           </v-list-item-content>
         </v-list-item>

         <v-card-actions>
           <v-btn text @click.prevent="fav(post)" v-if="!post.fav_status">お気に入り</v-btn>
           <v-btn text @click.prevent="fav(post)" v-else>お気に入り解除</v-btn>
           <v-btn text><router-link :to="{name: 'detail',params:{ postId: post.id}}" style="text-decoration: none; color: black;" >コメントする</router-link></v-btn>
         </v-card-actions>
       </v-card>
     </template>
   </div>
 </div>
</template>
<script>
import firebase from 'firebase/app' 
import "@firebase/auth"
export default {
 created() {
   firebase.auth().onAuthStateChanged(user => {
     user = user ? user : {}
     this.$store.commit('user/onAuthStateChanged', user)
     this.$store.commit('user/onAuthStatusChanged', user.uid ? true : false)
     this.$store.dispatch('user/myfollows')
     this.$store.dispatch('user/myfollowers')
   })
   let id = this.$route.params.userId
   this.$store.dispatch('user/getUser', id)
   
 },
 computed: {
   detailUser() {
     let array = this.$store.state.user.myfollows_users
     let check_user = this.$store.state.user.detail_user

     function checkAlreadyFollows(arr, id) {
       return arr.some(function(value) {
         return id === value.user_id
       })
     }
     if(checkAlreadyFollows(array, check_user.user_id)) {
       check_user.follow_status = true
     } else {
       check_user.follow_status = false
     }

     
     this.$store.state.user.myfollowers_users.forEach(user => {
       if(user.user_id === check_user.user_id) {
         check_user.followed_status = true
         Object.assign(check_user, check_user)
       }
     })
     return check_user
   }
 },
 methods: {
   follow(user) {
     this.$store.dispatch('user/follow', user)
   }
 }
}
</script>
state: {
   user: {},
   status: false,
   detail_user: {},
   myfollows_users: [],
   myfollowers_users: []
 },
 mutations: {
   onAuthStateChanged(state, user) {
     state.user = user
   },
   onAuthStatusChanged(state, status) {
     state.status = status
   },
   get(state, user) {
     state.detail_user = user
   },
   following(state, {detail_user, user}) {
     db.collection('follows').add({
       following_id: user.uid,
       follwed_id: detail_user.user_id
     })
     .then(() => {
       console.log('フォロー完了')
     })
     .catch(err => {
       console.log(err)
     })
   },
   remove_follow(state, {detail_user, user}) {
     db.collection('follows').where('following_id', '==', user.uid).get().then(elements => {
       elements.forEach(element => {
         let h = element.data()
         h.id = element.id
         if(h.follwed_id === detail_user.user_id){
           db.collection('follows').doc(h.id).delete().then(() => {
             state.myfollows_users = state.myfollows_users.filter(ele => ele.id !== h.id)
             console.log('フォロー解除')
           })
         }
       })
     })
   },
   myfollows(state, users) {
     state.myfollows_users = users
   },
   myfollowers(state, users) {
     state.myfollowers_users = users
   }
 },
 actions: {
   getUser({commit}, id) {
     db.collection('users').onSnapshot(users => {
       let obj_user = {}
       users.forEach(user => {
         let detail_user = user.data()
         detail_user.id = user.id
         if(detail_user.user_id === id) {
           Object.assign(obj_user, detail_user)
         }
       })
       commit('get', obj_user) 
     })
   },
   follow({commit, state}, detail_user) {
     const user = firebase.auth().currentUser
     if(state.myfollows_users.length) {
       state.myfollows_users.forEach(ele => {
         if(detail_user.user_id !== ele.user_id ) {
           commit('following', {detail_user: detail_user, user: user})
         } else {
           commit('remove_follow', {detail_user: detail_user, user: user})
         }
       })
     } else {
       commit('following', {detail_user: detail_user, user: user})
     } 
   },
   myfollows({commit}) {
     const user = firebase.auth().currentUser
     db.collection('follows').where('following_id', '==', user.uid).onSnapshot(elements => {
       let my_follows_users = []
       elements.forEach(element => {
         let record = element.data()
         record.id = element.id
         db.collection('users').where('user_id', '==', record.follwed_id).onSnapshot(users => {
           users.forEach(user => {
             let obj_user = user.data()
             obj_user.id = user.id
             my_follows_users.push(obj_user)
           })
         })
       })
       commit('myfollows', my_follows_users)
     })
   },
   myfollowers({commit}) {
     const user = firebase.auth().currentUser
     db.collection('follows').where('follwed_id', '==', user.uid).onSnapshot(elements => {
       let my_followers_users = []
       elements.forEach(element => {
         let record = element.data()
         record.id = element.id
         db.collection('users').where('user_id', '==', record.following_id).onSnapshot(users => {
           users.forEach(user => {
             let obj_user = user.data()
             obj_user.id = user.id
             my_followers_users.push(obj_user)
           })
         })
       })
       commit('myfollowers', my_followers_users)
     })
   }
 }

処理の流れとしては最初に言っている通り基本的にお気に入り機能に近い流れで処理されています。

なので、今回はお気に入り機能実装時とは少し違った点について書いていきます。

ポイント

1つ目はライフサイクルメソッドで呼び出しているdispatchについてです。

  firebase.auth().onAuthStateChanged(user => {
     user = user ? user : {}
     this.$store.commit('user/onAuthStateChanged', user)
     this.$store.commit('user/onAuthStatusChanged', user.uid ? true : false)
     this.$store.dispatch('user/myfollows')
     this.$store.dispatch('user/myfollowers')
   })

この2行をcreatedで実行していますが忘れてはいけないのが、firebase.auth().onAuthStateChangedの処理の中で呼び出すということ。

この外で呼び出してしまうと自分の情報が取得できないのでエラーが出てしまいます。

詳しくは公式を。

2つ目はactionsの中にあるfollow処理の部分です。commitする際にフォロー機能の場合はフォローするユーザーとフォローされる側のユーザーのidが必要になっていきます。

なのでcommitする際に実引数を2つほど渡していますが、少し書き方が変わっているので注意です。もちろんmutationsで受け取る際の引数の受け取り方についても変わっています。

3つ目は単純に自分がハマったところです。mutationsのremove_followの中の処理です。

.get().thenで取得しないと削除の処理なので正しく動いてくれないみたいです。自分は何も考えずにイケイケ全部リアルタイムやー!って感じだったのでハマりました。公式です。

db.collection('follows').where('following_id', '==', user.uid).onSnapshot(elements => {
db.collection('follows').where('following_id', '==', user.uid).get().then(elements => {

基本的に意識する部分はこれくらいかなと思っています。以上の処理でも既にフォローリストとフォロワーリストは取得できているので色々繋げていけるのではないかなと思っています。

自分はフォローリストの投稿一覧ページを作ってみたり、もし自分の事をフォローしているユーザーが居たりした場合は”フォローされています”の表示をつけたりしてみました。

実装してみて

やはり予想していた通りお気に入り機能の実装と処理の流れや考え方は似ているな〜と実装しながら感じていました。

その中でも違う処理をする部分が出てきたときは、ふむふむ〜ほーほー動かんな〜コレ。って感じですw

今回はその部分から学ぶことが多くありました。

次は何を実装しようかな〜と言った感じです。とりあえず今まで作成していたアプリに対してviewを作成していきます〜。

そんな感じで!!!

以上!