Post

[Vue] Vuex를 이용한 인스타그램 좋아요 기능

[Vue] Vuex를 이용한 인스타그램 좋아요 기능

@숙제

피드 사진을 누르면 Likes가 1 증가하고, 다시 누르면 Likes가 1 감소한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(store.js)

import { createStore } from "vuex";

const store = createStore({
  state () {
    return {
      likes: 36,
    }
  },
  mutations: {
    changeLikes(state, data){
      const isLikes = data[0];
      const idx = data[1];
      state.likes[idx] += (isLikes) ? 1 : -1;
    }
  }
})

export default store;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
(Post.vue)

<template>
  <div class="post">
    <div class="post-header">
      <div class="profile" :style="{ backgroundImage : `url('${post.userImage}')` }"></div>
        <span class="profile-name">{{ post.name }}</span>
      </div>
      <div class="post-body" :class="`${post.filter}`" :style="{ backgroundImage : `url('${post.postImage}')` }" @click="clickLikes"></div>
      <div class="post-content">
        <div class="icon">
          <button type="button" class="btn_like" :class="{on : Likes}" title="좋아요"></button>
          <button type="button" class="btn_comment" title="댓글 달기"></button>
          <button type="button" class="btn_dm" title="게시물 공유"></button>
          <button type="button" class="btn_keep" title="저장"></button>
        </div>

      <!-- <p class="like">{{ post.likes }} Likes</p> -->
      <p class="like">{{ $store.state.likes[idx] }} Likes</p>
      <p class="content"><strong>{{ post.name }}</strong> {{ post.content }}</p>
      <p class="date">{{ post.date }}</p>
    </div>
  </div>
</template>

<script>
export default {
  name: 'compPost',
  data(){
    return {
      Likes: false,
    }
  },
  methods: {
    clickLikes(){
      this.Likes = !this.Likes;
      this.$store.commit('changeLikes', [this.Likes, this.idx]);
    }
  },
  props: {
    data: Object,
    post: Object,
    idx: Number,
    filter: Text,
  }
}
</script>

@강의 정답

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(store.js)

import { createStore } from "vuex";

const store = createStore({
  state () {
    return {
      likes: [ 36, 0, 5 ],
      isLikes: [false, false, false],
    }
  },
  mutations: {
    changeLikes(state, data){
      state.likes[data] += (state.isLikes[data]) ? -1 : 1;
      state.isLikes[data] = !state.isLikes[data]
    }
  }
})

export default store;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
(Post.vue)

<template>
  <div class="post">
  <div class="post-header">
    <div class="profile" :style="{ backgroundImage : `url('${post.userImage}')` }"></div>
      <span class="profile-name">{{ post.name }}</span>
    </div>
    <div class="post-body" :class="`${post.filter}`" :style="{ backgroundImage : `url('${post.postImage}')` }" @click="clickLikes"></div>
    <div class="post-content">
      <div class="icon">
        <button type="button" class="btn_like" :class="{on : $store.state.isLikes[idx]}" title="좋아요"></button>
        <button type="button" class="btn_comment" title="댓글 달기"></button>
        <button type="button" class="btn_dm" title="게시물 공유"></button>
        <button type="button" class="btn_keep" title="저장"></button>
      </div>

      <!-- <p class="like">{{ post.likes }} Likes</p> -->
      <p class="like">{{ $store.state.likes[idx] }} Likes</p>
      <p class="content"><strong>{{ post.name }}</strong> {{ post.content }}</p>
      <p class="date">{{ post.date }}</p>
    </div>
  </div>
</template>

<script>
export default {
  name: 'compPost',
  methods: {
    clickLikes(){
      this.$store.commit('changeLikes', this.idx);
    }
  },
  props: {
    data: Object,
    post: Object,
    idx: Number,
    filter: Text,
  }
}
</script>

@응용

게시물 등록 시 Likes 배열을 추가해보자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(store.js)

import { createStore } from "vuex";

const store = createStore({
  state () {
    return {
      likes: [ 36, 0, 5 ],
      isLikes: [false, false, false],
    }
  },
  mutations: {
    changeLikes(state, data){
      state.likes[data] += (state.isLikes[data]) ? -1 : 1;
      state.isLikes[data] = !state.isLikes[data]
    },
    LikesItemShift(state){
      state.likes.unshift(0);
      state.isLikes.unshift(false);
    }
  }
})

export default store;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
(App.vue)

<template>
  <div class="header">
    <h1 class="logo"><a href="#" title="vue-stagram"></a></h1>
    <div class="header-button-left">
      <a href="#" v-if="tab==1 || tab==2" @click="tab=0">Cancel</a>
    </div>
    <div class="header-button-right">
      <a href="#" v-if="tab==1" @click="tab=2">Next</a>
      <a href="#" v-if="tab==2" @click="publish">Write</a>
    </div>
  </div>

  <Container :post="post" :tab="tab" :SelectFilter="SelectFilter" :uploadImage="uploadImage" @write=" n => uploadText = n " />
  <button @click="more();" type="button" class="btn_more" v-if="tab==0">더보기</button>

  <div class="footer">
    <div class="file_wrap" v-if="tab==0">
      <input type="file" id="file" class="inputfile" accept="image/*" multiple @change="upload">
      <label for="file" class="input-plus" title="이미지 업로드"></label>
    </div>
  </div>

</template>

<script>
import post from './assets/data.js'
import Container from './components/Container.vue'
import axios from 'axios'

export default {
  name:'App',
  data(){
    return {
      post : post,
      get: 0,
      tab: 0,
      uploadImage: '',
      uploadText: '',
      SelectFilter: '',
    }
  },
  methods: {
    publish(){
      const newPost = {
        name: "aluvy",
        userImage: "./img/profile1.jpg",
        postImage: this.uploadImage,
        likes: 0,
        date: "May 15",
        liked: false,
        content: this.uploadText,
        filter: this.SelectFilter,
      };
      this.post.unshift(newPost);
      this.tab = 0;
      this.$store.commit('LikesItemShift');
    },
    upload(e){
      const file = e.target.files;  // 업로드한 파일이 담겨있음
      const type = "image";

      if( file[0].type.indexOf(type) == -1 ){   // 문자열이 존재하지 않으면 -1 리턴
        alert("이미지만 업로드 해주세요.");
        return;
      }

      const url = URL.createObjectURL(file[0]); // 업로드한 파일의 임시 URL
      this.tab = 1;
      this.uploadImage = url;

    },
    more(){
      if( this.get > 1 ){

        const btn_more = document.querySelector(".btn_more");
        const p = document.createElement("p");
        p.innerText = "게시물이 없습니다.";
        p.classList.add("no_post");
        btn_more.after(p);
        btn_more.remove();

        return;
      }
      axios.get(`https://codingapple1.github.io/vue/more${this.get}.json`)
      .then( result => {
        this.post.push(result.data);
        this.get++;
      })
      .catch( error =>{
        // 요청 실패 시
        console.log(error);
      })
    }
  },
  components:{
    Container,
  },
  mounted(){
    this.emitter.on('FilterName', (a)=>{
      this.SelectFilter = a;
      console.log(this.SelectFilter);
    });
  }
}
</script>
This post is licensed under CC BY 4.0 by the author.

Trending Tags