본문으로 건너뛰기

TanStack DB Vue 어댑터

설치​

npm install @tanstack/vue-db

Vue 컴포저블​

Vue Adapter에서 사용할 수 있는 전체 컴포저블 목록은 Vue Functions Reference를 참조할 수 있습니다.

쿼리 작성(필터링, 조인, 집계 등)에 관한 종합적인 문서는 Live Queries Guide를 참조할 수 있습니다.

기본 사용법​

useLiveQuery​

useLiveQuery 컴포저블은 데이터가 변경될 때 컴포넌트를 자동으로 업데이트하는 라이브 쿼리를 생성합니다. 다음과 같은 반응형 computed ref를 반환합니다.

<script setup>
import { useLiveQuery } from '@tanstack/vue-db'
import { eq } from '@tanstack/db'

const { data, isLoading } = useLiveQuery((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.completed, false))
.select(({ todos }) => ({ id: todos.id, text: todos.text }))
)
</script>

<template>
<div v-if="isLoading">Loading...</div>
<ul v-else>
<li v-for="todo in data" :key="todo.id">{{ todo.text }}</li>
</ul>
</template>

참고: 모든 반환 값(data, isLoading, status 등)은 computed ref이므로 .value를 사용해 <script>에서 접근하지만, <template>에서는 직접 접근합니다.

useLiveInfiniteQuery​

라이브 업데이트가 지원되는 정렬 및 페이지 매김 데이터에는 useLiveInfiniteQuery를 사용합니다.

<script setup>
import { ref } from 'vue'
import { useLiveInfiniteQuery } from '@tanstack/vue-db'
import { eq } from '@tanstack/db'

const category = ref('news')
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useLiveInfiniteQuery(
(q) =>
q
.from({ posts: postsCollection })
.where(({ posts }) => eq(posts.category, category.value))
.orderBy(({ posts }) => posts.createdAt, 'desc'),
{ pageSize: 20 },
[category],
)
</script>

<template>
<article v-for="post in data" :key="post.id">
{{ post.title }}
</article>
<button
v-if="hasNextPage"
:disabled="isFetchingNextPage"
@click="fetchNextPage()"
>
Load more
</button>
</template>

fetchNextPage()는 페이지 요청이 완료된 후 resolve되는 프로미스를 반환합니다. 실패는 반환된 error ref를 통해 노출되며 프로미스를 reject하지 않습니다.

쿼리에는 orderBy가 포함되어야 합니다. 종속성 배열은 쿼리 함수 형식에서만 사용할 수 있습니다. 정렬된 미리 생성된 라이브 쿼리 컬렉션을 직접 전달할 수도 있습니다.

종속성 배열​

useLiveQuery 컴포저블은 마지막 매개변수로 선택적 종속성 배열을 받습니다. 배열의 반응형 값이 변경되면 쿼리가 다시 생성되고 재실행됩니다.

종속성 배열을 사용하는 경우​

쿼리가 외부 반응형 값(ref, props 또는 반응형 객체)에 의존하는 경우 종속성 배열을 사용합니다.

<script setup>
import { ref } from 'vue'
import { useLiveQuery } from '@tanstack/vue-db'
import { gt } from '@tanstack/db'

const minPriority = ref(5)

const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => gt(todos.priority, minPriority.value)),
[minPriority] // Pass the ref directly, it will be unwrapped automatically
)
</script>

<template>
<div>{{ data.length }} high-priority todos</div>
</template>

중요: 종속성 배열에는 ref를 함수가 아닌 직접 전달합니다. Vue가 자동으로 추적합니다.

종속성이 변경되면 발생하는 일​

종속성 값이 변경되면:

  1. 이전 라이브 쿼리 컬렉션이 정리됩니다
  2. 업데이트된 값으로 새 쿼리가 생성됩니다
  3. 컴포넌트가 새 데이터로 다시 렌더링됩니다
  4. 컴포저블이 로딩 상태를 다시 표시합니다

모범 사례​

쿼리에서 사용하는 모든 외부 ref를 포함합니다:

<script setup>
import { ref } from 'vue'
import { useLiveQuery } from '@tanstack/vue-db'
import { eq, and } from '@tanstack/db'

const userId = ref(1)
const status = ref('active')

// Good - all refs in deps array
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => and(
eq(todos.userId, userId.value),
eq(todos.status, status.value)
)),
[userId, status] // Pass refs directly
)

// Bad - missing dependencies
const { data: badData } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.userId, userId.value)),
[] // Missing userId!
)
</script>

<template>
<div>{{ data.length }} todos</div>
</template>

props와 함께 사용:

<script setup>
import { toRef } from 'vue'
import { useLiveQuery } from '@tanstack/vue-db'
import { eq } from '@tanstack/db'

const props = defineProps<{ userId: number }>()

// Option 1: Convert prop to ref
const userIdRef = toRef(props, 'userId')
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.userId, userIdRef.value)),
[userIdRef]
)

// Option 2: Use a getter function for the prop
const { data: data2 } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.userId, props.userId)),
[() => props.userId] // Getter function for non-ref values
)
</script>

<template>
<div>{{ data.length }} todos</div>
</template>

정적 쿼리에는 빈 배열:

<script setup>
import { useLiveQuery } from '@tanstack/vue-db'

// No external dependencies - query never changes
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection }),
[]
)
</script>

<template>
<div>{{ data.length }} todos</div>
</template>

외부 종속성이 없는 쿼리에는 배열 생략:

<script setup>
import { useLiveQuery } from '@tanstack/vue-db'

// Same as above - no deps needed
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
)
</script>

<template>
<div>{{ data.length }} todos</div>
</template>

미리 생성된 컬렉션 사용​

기존 컬렉션을 useLiveQuery에 전달할 수도 있습니다. 이는 컴포넌트 간에 쿼리를 공유할 때 유용합니다:

<script setup>
import { ref } from 'vue'
import { createLiveQueryCollection } from '@tanstack/db'
import { useLiveQuery } from '@tanstack/vue-db'
import { eq } from '@tanstack/db'

// Create collection outside component or in a composable
const todosQuery = createLiveQueryCollection({
query: (q) => q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.active, true)),
startSync: true
})

// Use the pre-created collection
const { data, collection } = useLiveQuery(todosQuery)

// Or use a reactive ref to switch between collections
const currentQuery = ref(todosQuery)
const { data: reactiveData } = useLiveQuery(currentQuery)
</script>

<template>
<div>{{ data.length }} todos</div>
</template>