Vue
Vue는 외부에서 관리되는 상태를 shallowRef에 담는 방식을 권장합니다. Vue는 .value 컨테이너만 추적하고, 내부 값의 소유권은 외부 store에 남겨둡니다. 이는 root 상태 값을 교체하는 @ilokesto/store 방식과 잘 맞습니다.
composable 만들기
import { onUnmounted, shallowRef, type ShallowRef } from "vue";
import { Store } from "@ilokesto/store";
export function useStoreRef<T>(store: Store<T>): ShallowRef<Readonly<T>> {
const state = shallowRef(store.getState()) as ShallowRef<Readonly<T>>;
const unsubscribe = store.subscribe(() => {
state.value = store.getState();
});
onUnmounted(unsubscribe);
return state;
}이 composable은 setup() 또는 컴포넌트 lifecycle 안에서 실행되는 다른 composable에서 호출하세요. 컴포넌트 밖에서 구독한다면 unsubscribe를 직접 보관하고 호출해야 합니다.
컴포넌트에서 사용하기
<script setup lang="ts">
import { Store } from "@ilokesto/store";
import { useStoreRef } from "./useStoreRef";
type CounterState = { count: number };
const counterStore = new Store<CounterState>({ count: 0 });
const counter = useStoreRef(counterStore);
function increment() {
counterStore.setState((prev) => ({ count: prev.count + 1 }));
}
</script>
<template>
<button type="button" @click="increment">
Count: {{ counter.count }}
</button>
</template>template에서는 ref unwrapping 덕분에 counter.count로 읽을 수 있습니다. 일반 TypeScript 코드에서는 counter.value.count로 읽으세요.
왜 shallowRef인가요?
@ilokesto/store가 상태 객체를 소유합니다. Vue는 store가 root 값을 교체할 때만 반응하면 됩니다. shallowRef는 내부 상태를 깊게 proxy로 변환하지 않고, state.value가 교체될 때 Vue 업데이트를 발생시킵니다.
Mutation 주의
state.value를 직접 수정하지 마세요. 미들웨어 적용, Object.is 비교, 구독자 알림이 모두 동작하도록 store.setState()를 통해 업데이트하세요.