목차
Vue 기본 구조
- 기본 구조 설명 - Composition API 방식( 다른 방식으로는 Option API 방식이있음 export default{} )
<script setup>
// 사용하려는 store를 import
import { useTodosStore } from '@/stores/todos'
// 사용 선언
const todos = useTodosStore()
// --- state의 값을 반응형으로 참조하는 방법
const { todos, count } = storeToRefs(todos)
// --- action은 구조분해하여 사용할 수 있음.
const { increment } = todos
// --- state의 값 수정 방법 3가지
// 1. 값 수정
counter.count++
// 2. $patch 이용
counter.$patch({ count: counter.count + 1 })
// 3. action 이용
counter.increment()
</script>
<template>
<!-- Store에 직접 state에 접근 -->
<div>Current Count: {{ counter.count }}</div>
</template>
Pinia 기본 구조
- 기본 구조 설명 Option Store방식
- 기존 방식처럼 state, getters, actions를 직관적으로 표현한다.
- stores/todos-store.js
-
import { defineStore } from 'pinia' export const useTodosStore = defineStore('todos', { //-- store의 이름 state: () => ({ /** @type {{ text: string, id: number, isFinished: boolean }[]} */ todos: [], /** @type {'all' | 'finished' | 'unfinished'} */ filter: 'all', // type will be automatically inferred to number nextId: 0, count: 0 }), getters: { finishedTodos(state) { return state.todos.filter((todo) => todo.isFinished) }, unfinishedTodos(state) { return state.todos.filter((todo) => !todo.isFinished) }, filteredTodos(state) { if (this.filter === 'finished') { return this.finishedTodos } else if (this.filter === 'unfinished') { return this.unfinishedTodos } return this.todos }, }, actions: { increment() { this.count++ }, addTodo(text) { this.todos.push({ text, id: this.nextId++, isFinished: false }) }, }, })
State (Vue)
- 기본적으로 인스턴스를 통해 state에 접근하여 읽고 쓸 수 있음
- 단, state에 먼저 정의가 되어있어야함.
const store = useStore() store.count++
- 초기화 (Option Store방식만 가능, Setup Store 방식은 직접 생성해야함.)
-
const store = useStore() store.$reset()
- state 변경
- 직접 변경
const store = useStore() store.count++
- patch사용
const store = useStore() store.$patch({ count: store.count + 1, age: 120, name: 'DIO', })
- array를 수정(push, remove, splice 등) 해야하는 경우라면 mutation을 그룹화 하는 함수도 가능함
const store = useStore() store.$patch((state) => { state.items.push({ name: 'shoes', quantity: 1 }) state.hasChanged = true })
- 직접 변경
- state 변경시 주의
-
// ❌ $state 를 대신할 수 없음 store.$state = { count: 24 } // ⭕ 내부적으로 $patch()를 호출 store.$patch({ count: 24 })
- state 변경 추적 (subscribe, 구독)
-
<script setup> const someStore = useSomeStore() // 이 추적은 component가 unmount된 후에도 유지됨 someStore.$subscribe(callback, { detached: true }) </script>
State (Pinia)
- state 의 기본 구조 (Option Store 방식)
-
state: () => { return { // 처음에는 비어있는 list의 경우 userList: [] as UserInfo[], // 아직 load되지 않은 데이터의 경우 user: null as UserInfo | null, } }, interface UserInfo { name: string age: number }
- State 인터페이스를 이용하는 경우 (Option Store 방식)
-
interface State { userList: UserInfo[] user: UserInfo | null } export const useUserStore = defineStore('user', { state: (): State => { return { userList: [], user: null, } }, }) interface UserInfo { name: string age: number }
- state 변경 추적 (subscribe, 구독)
-
import { MutationType } from 'pinia' cartStore.$subscribe((mutation, state) => { mutation.type // 'direct' | 'patch object' | 'patch function' // same as cartStore.$id mutation.storeId // 'cart' // only available with mutation.type === 'patch object' mutation.payload // patch object passed to cartStore.$patch() // 변경될 때마다 전체 상태를 로컬 스토리지에 유지 localStorage.setItem('cart', JSON.stringify(state)) })
Getters (Vue)
- getters에 접근
-
<script setup> import { useCounterStore } from './counterStore' const store = useCounterStore() </script> <template> <p>Double count is {{ store.doubleCount }}</p> </template>
- getters에 인수(argument) 전달
- computed 속성이므로 매개변수를 전달할 수는 없으나, 허용하는 방법은 있음.
<script setup> import { storeToRefs } from 'pinia' import { useUserListStore } from './store' const userList = useUserListStore() // <script setup> 내에서 // getUserById.value에 접근 해야함. const { getUserById } = storeToRefs(userList) </script> <template> <p>User 2: {{ **getUserById(2)** }}</p> </template>
- 이 방법을 사용하면 getter는 더이상 cashed 되지 않음, 단순히 호출하는 함수
Getters (Pinia)
- vue의 computed, vuex의 getters와 유사
- getters 의 기본 구조
-
export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, }), getters: { // 반환 타입을 자동으로 number로 추론함 doubleCount(state) { return state.count * 2 }, // 반환 타입은 "반드시" 명시적으로 설정해야함 doublePlusOne(): number { // 전체 store에 대한 자동 완성 및 typings ✨ return this.doubleCount + 1 }, }, })
- getters에 인수(argument) 전달
- computed 속성이므로 매개변수를 전달할 수는 없으나, 허용하는 방법은 있음.
export const useStore = defineStore('main', { getters: { getUserById: (state) => { return (userId) => state.users.find((user) => user.id === userId) }, }, })
- 이 방법을 사용하면 getter는 더이상 cashed 되지 않음, 단순히 호출하는 함수
- 다른 store의 getter 에 access
-
import { useOtherStore } from './other-store' export const useStore = defineStore('main', { state: () => ({ // ... }), getters: { otherGetter(state) { const otherStore = useOtherStore() return state.localData + otherStore.data }, }, })
-
Actions (Vue)
- actions의 기본 구조
-
<script setup> const store = useCounterStore() // store에서 action을 호출 store.randomizeCounter() </script> <template> <button @click="store.randomizeCounter()">Randomize</button> </template>
- action 변경 추적 (subscribe, 구독)
-
<script setup> const someStore = useSomeStore() // 이 구독은 component가 unmount된 후에도 유지 됨 someStore.$onAction(callback, true) </script>
Actions (Pinia)
- actions 의 기본 구조
-
export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, }), actions: { // this에 의존하므로 화살표 함수 사용할 수 없음 increment() { this.count++ }, randomizeCounter() { this.count = Math.round(100 * Math.random()) }, }, })
- 다른 store의 actions 접근
-
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('User must be authenticated') } }, }, })
- action 변경 추적 (subscribe, 구독)
-
const unsubscribe = someStore.$onAction( ({ name, // action의 이름 store, // store 인스턴스(예시:someStore) args, // action에 전달되는 매개변수 배열 after, // action 반환 혹은 resolve 후의 hook onError, // action throw 혹은 rejenct 시의 hook }) => { // 특정 action 호출에 대한 공유 변수(shared variable) const startTime = Date.now() // 이 코드는 store에 대한 action 실행 전 트리거 됨 console.log(`Start "${name}" with params [${args.join(', ')}].`) // 이 코드는 action이 성공하고 완전히 실행된 후에 트리거 됨 // promised를 반환 할때까지 기다림 after((result) => { console.log( `Finished "${name}" after ${ Date.now() - startTime }ms.\nResult: ${result}.` ) }) // action throw 혹은 rejenct 시에 실행 onError((error) => { console.warn( `Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.` ) }) } ) // listner를 수동으로 제거 unsubscribe()
반응형