본문 바로가기
IT/Pinia

Pinia - Action ( Option Store )

by 2T1 2023. 1. 2.
이 글은 Vue 3의 공식 Store | Pinia 를 보고 정리한 글입니다.
더 자세한 내용을 원하시면 공식 문서를 참고하세요.

 

컴포넌트의 method와 동일한 일을 합니다.

이들은 defineStore()에서 actions속성으로 정의할 수 있으며, 처리해야 할 작업의 로직을 정의하는데 완벽합니다 :

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
  }),
  actions: {
    // `this`에 의존하므로, 화살표 함수를 사용할 수 없음.
    increment() {
      this.count++
    },
    randomizeCounter() {
      this.count = Math.round(100 * Math.random())
    },
  },
})

getter와 마찬가지로 액션은 전체 입력(및 자동 완성 ✨) 지원과 함께 this를 통해 전체 스토어 인스턴스에 접근할 수 있습니다. getter와 달리 actions은 비동기식일 수 있으며, action 내에서 API 호출이나 다른 action을 await할 수 있습니다!

 

 

 

여기에 Mande를 사용한 예제가 있습니다. Promise를 얻기 위해 어떤 라이브러리를 사용하는지는 중요하지 않습니다. 네이티브 fetch 함수(브라우저만 해당)를 사용할 수도 있습니다:

import { mande } from 'mande'

const api = mande('/api/users')

export const useUsers = defineStore('users', {
  state: () => ({
    userData: null,
    // ...
  }),

  actions: {
    async registerUser(login, password) {
      try {
        this.userData = await api.post({ login, password })
        showTooltip(`다시 오신 것을 환영합니다, ${this.userData.name}!`)
      } catch (error) {
        showTooltip(error)
        // 폼(form) 컴포넌트가 오류를 표시하도록 함.
        return error
      }
    },
  },
})

또한 원하는 인자를 자유롭게 설정하고, 무엇이든 return 할 수 있습니다. action을 호출하면 모든 것이 자동으로 추론됩니다.

 

 

 

action 은 method처럼 호출됩니다 :

export default defineComponent({
  setup(){
    const store = useCounterStore()
    
    //스토어의 action을 method 처럼 호출
    store.randomizeCounter()
    
    return {}
  }
})

 

다른 store의 action에 접근

import { useAuthStore } from './auth-store'

export const useSettingsStore = defineStore('settings', {
  state: () => ({
    preferences: null,
    // ...
  }),
  actions: {
    async fetchUserPreferences() {
      const auth = useAuthStore()
      if (auth.isAuthenticated) {
        this.preferences = await fetchPreferences()
      } else {
        throw new Error('인증이 필요합니다!')
      }
    },
  },
})

setup()에서 사용

export default {
  setup() {
    const store = useCounterStore()

    store.randomizeCounter()
  },
}

Option API에서 사용

// 예제 파일 경로:
// ./src/stores/counter.js

import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0
  }),
  actions: {
    increment() {
      this.count++
    }
  }
})

setup() hook을 사용하면 Option API에서 피니아를 더 쉽게 사용할 수 있습니다. 추가 map Helper 함수가 필요하지 않습니다.

import { useCounterStore } from '../stores/counter'

export default {
  setup() {
    const counterStore = useCounterStore()

    return { counterStore }
  },
  methods: {
    incrementAndPrint() {
      this.counterStore.increment()
      console.log('숫자세기:', this.counterStore.count)
    },
  },
}

 

 

 

Composition API를 전혀 사용하지 않고 mapActions() Helper를 사용하여 Component의 method에 action 속성을 mapping 할 수 있습니다 :

import { mapActions } from 'pinia'
import { useCounterStore } from '../stores/counter'

export default {
  methods: {
    // 컴포넌트 내부에서 `this.increment()`로 접근할 수 있게 함.
    // `store.increment()`처럼 호출하는 것과 동일.
    ...mapActions(useCounterStore, ['increment']),
    
    // 위와 같지만 `this.myOwnName()`으로 등록.
    ...mapActions(useCounterStore, { myOwnName: 'increment' }),
  },
}

action 구독하기

store.$onAction()에 콜백을 전달해 액션과 그 결과를 감시할 수 있으며, 액션보다 먼저 실행됩니다.

 

after는 프라미스(promise)를 처리하고, 액션이 해결(resolve)된 후, 함수를 실행할 수 있도록 합니다. 비슷한 방식으로 onError를 사용하면, 작업이 실패(throw)되거나 거부(reject)되는 경우, 함수를 실행할 수 있습니다. 이는 Vue 문서에서 언급하는 팁과 유사하게 런타임에 오류를 추적하는 데 유용합니다.

 

다음은 액션을 실행하기 전과 해결/거부 이후를 콘솔에 기록하는 예제입니다.

const unsubscribe = someStore.$onAction(
  ({
    name, // 액션의 이름.
    store, // 스토어 인스턴스, `someStore`와 같음.
    args, // 액션으로 전달된 인자로 이루어진 배열.
    after, // 액션에서 반환 또는 해결 이후의 훅.
    onError, // 액션에서 실패 또는 거부될 경우의 훅.
  }) => {
    // 이 특정 액션 호출에 대한 공유 변수.
    // 역자설명: 이 액션의 훅에서 참조하게 되는 클로저 변수 개념.
    const startTime = Date.now()
    
    // 이곳은 `store`의 액션이 실행되기 전에 트리거됨.
    console.log(`"${name}"가 [${args.join(', ')}] 인자를 전달받아 시작됩니다.`)

    // 액션이 성공하고 완전히 실행된 후에 트리거됨.
    // 프라미스 반환을 대기.
    after((result) => {
      console.log(
        `"${name}"가 ${
          Date.now() - startTime
        }ms 후 종료됬습니다.\n결과: ${result}.`
      )
    })

    // 액션이 실패하거나 프라미스가 거부되면 트리거 됨.
    onError((error) => {
      console.warn(
        `"${name}"가 ${
          Date.now() - startTime
        }ms 후 실패했습니다.\n애러: ${error}.`
      )
    })
  }
)

// 리스너를 수동으로 제거.
unsubscribe()

 

 

기본적으로 액션 구독은 컴포넌트에 추가된(스토어가 컴포넌트의 setup() 내부에 있는) 경우에 바인딩됩니다. 따라서 컴포넌트가 마운트 해제되면 자동으로 제거됩니다. 컴포넌트가 마운트 해제된 후에도 이를 유지하려면, 두 번째 인수로 현재 컴포넌트에서 액션 구독을 분리하는 true를 전달합니다:

export default {
  setup() {
    const someStore = useSomeStore()

    // 이 구독은 컴포넌트가 마운트 해제된 후에도 유지됨.
    someStore.$onAction(callback, true)

    // ...
  },
}

'IT > Pinia' 카테고리의 다른 글

Vue3 ) script setup  (0) 2023.03.28
Pinia - Getter ( Option Store )  (0) 2023.01.02
Pinia - State ( Option Store )  (0) 2023.01.02
Pinia  (0) 2022.12.30