Vuex (3) getters, actions

서영·2021년 10월 5일
0

Vue

목록 보기
3/6

actions 내부에서 state에 접근하지 마라. state를 연산하든 다루든 하는거는 무조건 mutation 안에서 해라.

1. getters

똑같은 코드, 똑같은 로직을 가진 컴포넌트가 중복되었을 때 사용하는 것? getters

  • TheCounter.vue는 state인 counter를 출력하는 컴포넌트이다. 이와 거의 비슷한 역할을 하는 컴포넌트 FavoriteCounter.vue를 생성했다면... → 중복이 많아지면 프로젝트가 커질수록 번잡해질 것이다..

FavoriteValue.vue

<template>
    <h3>{{counter}}</h3>
    <p>We do more...</p>
</template>

<script>
export default {
    computed:{
        counter(){
            return this.$store.state.counter * 2;
        }
    }
}
</script>

<style>

</style>

이를 다루기 위해 main.js로 가서 getters를 사용해보자.


main.js

getters: {
        finalCounter(state) {
            return state.counter * 3;
        },
    }
  • 모든 getter는 method를 가짐

TheCounter.vue & FavoriteValue.vue

<template>
    <h3>{{counter}}</h3>
</template>

<script>
export default {
    computed:{
        counter(){
            return this.$store.getters.finalCounter;
        }
    }
}
</script>

<style>

</style>

FavoriteValue.vue에서는 counter값이 100 초과 시 100으로, 0 미만 시 0으로 나타내주고 싶다.

main.js

getters: {
        finalCounter(state) {
            return state.counter * 3;
        },
        normalizedCounter(state, getters) {
            const finalCounter = getters.finalCounter;
            if (finalCounter < 0) {
                return 0;
            } if (finalCounter > 100) {
                return 100;
            }
            return finalCounter;
        }
  • normalizedCounter에서 보듯이 getter는 한 줄 이상의 코드를 가질 수 있고, 반드시 값을 return 해야한다.
  • (꿀팁)여기서는 두 개의 argument를 가지고 있지만 state는 사용하지 않고 있어서 vue에서 경고문구를 줄 수도 있다. 이럴 땐 _ 를 쓰면 아무말 안한다.
  • 두번째 인자인 getters를 통해 다른 getter를 불러올 수도 있다. 여기서는 const로 선언한 finalCounter가 getter로 선언한 finalCounter() 메소드와 같은 연산을 하기 때문에 인자로 받아오는 게 더 효율적이고 엘레강스하다.

결과는 다음과 같다.

  • main.js 전체코드
    import { createApp } from 'vue';
    import { createStore } from 'vuex';
    
    import App from './App.vue';
    
    const store = createStore({
        state() {
            return {
                counter: 0
            };
        },
        mutations: {
            increment(state) {
                state.counter += 2;
            },
            increase(state, payload) {
                state.counter = state.counter + payload.value;
            }
        },
        getters: {
            finalCounter(state) {
                return state.counter * 3;
            },
            normalizedCounter(_, getters) { //안쓰는 인자는 _ 하면 에러메시지 안뜬대 -> 211005수정) 에러 뜨는데?
                const finalCounter = getters.finalCounter;
                if (finalCounter < 0) {
                    return 0;
                } if (finalCounter > 100) {
                    return 100;
                }
                return finalCounter;
            }
        }
    });
    
    const app = createApp(App);
    
    app.use(store);
    
    app.mount('#app');

2. actions

  • mutation은 반드시 synchronous 해야한다. 즉 비동기를 쓰지 않는 것이 좋다.

이유? → 만약 여러 mutation이 실행될때 모든 mutation은 최신 state를 가지고 수행한다. 만약 다른 mutation이 commit되었을 때 아직 안끝났다면 state값에 있어서 error가 날 수 있기 때문이다.

(mutation에서 setTimeout 써도 되긴 한데 그러면 나중에 문제생길 수 있으니 쓰지 말자)

🗣️ : 그러면 비동기를 수행하려면 어떻게 해야할까???

actions를 사용하면 비동기가 가능하다!

  • getters와 mutations 사이에 쓰면 된다.
  • actions는 object로 methods를 가지는데 메소드명은 다른 곳에서 구현한 함수명과 같아도 된다. → 오히려 이게 복잡한 코드 사이에서 더 이해하기 쉬울 것이다.

main.js

actions: {
        increment(context) {
            setTimeout(function () {
                context.commit('increment')
            }, 2000);
        },
        increase(context, payload) {
            context.commit('increase', payload);
        }
    },
  • actions의 메소드는 argument로 context를 가진다. context는 다른 모든 기능과 마찬가지로 Vuex에 의해서 자동적으로 얻어진다.
  • mutation에서 구현했던 메소드를 commit으로 부를 수 있다.
  • increase()와 같이 context, payload 두 가지 인자를 받을 수 있다.

App.vue

methods: {
    addOne() {
      this.$store.commit('increase', { value: 10 }); //commit 표현법1

      this.$store.dispatch({ 
        type:'increment',
      });
    },
  },
  • 다음과 같이 dispatch 를 통해 actions를 사용할 수 있다.
  • 결과) Add 2 버튼을 누르면 2초 뒤에 counter가 2 증가한다.

main.js 전체코드

    import { createApp } from 'vue';
    import { createStore } from 'vuex';
    
    import App from './App.vue';
    
    const store = createStore({
        state() {
            return {
                counter: 0
            };
        },
        mutations: {
            increment(state) {
                state.counter += 2;
            },
            increase(state, payload) {
                state.counter = state.counter + payload.value;
            }
        },
        actions: {
            increment(context) {
                setTimeout(function () {
                    context.commit('increment')
                }, 2000);
            },
            increase(context, payload) {
                context.commit('increase', payload);
            }
        },
        getters: {
            finalCounter(state) {
                return state.counter * 3;
            },
            normalizedCounter(getters) {
                const finalCounter = getters.finalCounter;
                if (finalCounter < 0) {
                    return 0;
                } if (finalCounter > 100) {
                    return 100;
                }
                return finalCounter;
            }
        }
    });
    
    const app = createApp(App);
    
    app.use(store);
    
    app.mount('#app');
profile
꾸준히 공부하기

0개의 댓글