欢迎阅读 TanStack DB 文档。
TanStack DB 是一个响应式客户端存储,用于在同步时构建超快速的应用程序。它通过集合、实时查询和乐观突变扩展了 TanStack Query。
TanStack DB 的工作原理如下:
// 定义要加载数据的集合
const todoCollection = createCollection({
// ...你的配置
onUpdate: updateMutationFn
})
const Todos = () => {
// 使用实时查询绑定数据
const { data: todos } = useLiveQuery((q) =>
q
.from({ todo: todoCollection })
.where(({ todo }) => todo.completed)
)
const complete = (todo) => {
// 立即应用乐观状态
todoCollection.update(todo.id, (draft) => {
draft.completed = true
})
}
return (
<ul>
{todos.map(todo =>
<li key={ todo.id } onClick={() => complete(todo) }>
{ todo.text }
</li>
)}
</ul>
)
}
// 定义要加载数据的集合
const todoCollection = createCollection({
// ...你的配置
onUpdate: updateMutationFn
})
const Todos = () => {
// 使用实时查询绑定数据
const { data: todos } = useLiveQuery((q) =>
q
.from({ todo: todoCollection })
.where(({ todo }) => todo.completed)
)
const complete = (todo) => {
// 立即应用乐观状态
todoCollection.update(todo.id, (draft) => {
draft.completed = true
})
}
return (
<ul>
{todos.map(todo =>
<li key={ todo.id } onClick={() => complete(todo) }>
{ todo.text }
</li>
)}
</ul>
)
}
集合是可以填充数据的类型化对象集。它们旨在将加载数据到应用程序与将数据绑定到组件解耦。
集合可以通过多种方式填充,包括:
一旦将数据放入集合中,就可以在组件中使用实时查询跨集合查询数据。
实时查询用于从集合中查询数据。实时查询是响应式的:当基础数据发生变化并可能影响查询结果时,结果会增量更新并从查询中返回,从而触发重新渲染。
TanStack DB 实时查询使用 d2ts 实现,这是一个差分数据流的 Typescript 实现。这使得查询结果可以增量更新(而不是通过重新运行整个查询)。这使得它们速度极快,通常在亚毫秒级别,即使对于高度复杂的查询也是如此。
实时查询支持跨集合连接。这使您可以:
每个查询都会返回另一个也可以查询的集合。
集合支持 insert、update 和 delete 操作。默认情况下,调用它们时会触发相应的 onInsert、onUpdate、onDelete 处理程序,这些处理程序负责将突变写入后端。
// 定义具有持久性处理程序的集合
const todoCollection = createCollection({
id: "todos",
// ... 其他配置
onUpdate: async ({ transaction }) => {
const {original, changes} = transaction.mutations[0]
await api.todos.update(original.id, changes)
},
})
// 立即应用乐观状态
todoCollection.update(todo.id, (draft) => {
draft.completed = true
})
// 定义具有持久性处理程序的集合
const todoCollection = createCollection({
id: "todos",
// ... 其他配置
onUpdate: async ({ transaction }) => {
const {original, changes} = transaction.mutations[0]
await api.todos.update(original.id, changes)
},
})
// 立即应用乐观状态
todoCollection.update(todo.id, (draft) => {
draft.completed = true
})
集合内部不会直接改变集合数据,而是将其同步/加载的数据视为不可变的,并维护一组单独的本地突变作为乐观状态。当实时查询从集合中读取数据时,它们会看到一个本地视图,该视图将本地乐观突变叠加在不可变的同步数据之上。
乐观状态会一直保持,直到 onUpdate(在本例中)处理程序解析为止 - 此时数据将持久化到服务器并同步回本地集合。
如果处理程序引发错误,则回滚乐观状态。
突变基于 Transaction 原语。
对于简单的状态更改,直接改变集合并使用操作符处理程序进行持久化就足够了。
但是对于更复杂的用例,你可以直接使用 createOptimisticAction 创建自定义操作,或使用 createTransaction 创建自定义事务。这使你可以执行诸如跨多个集合执行具有多个突变的事务、执行具有中间回滚的链式事务等操作。
例如,在以下代码中,mutationFn 首先使用 await api.todos.update(updatedTodo) 将写入发送到服务器,然后调用 await collection.refetch() 以使用 TanStack Query 触发集合内容的重新获取。当第二个 await 解析时,集合将与最新更改保持同步,并且乐观状态将被安全地丢弃。
const updateTodo = createOptimisticAction<{id: string}>({
onMutate,
mutationFn: async ({ transaction }) => {
const { collection, modified: updatedTodo } = transaction.mutations[0]
await api.todos.update(updatedTodo)
await collection.refetch()
},
})
const updateTodo = createOptimisticAction<{id: string}>({
onMutate,
mutationFn: async ({ transaction }) => {
const { collection, modified: updatedTodo } = transaction.mutations[0]
await api.todos.update(updatedTodo)
await collection.refetch()
},
})
这结合起来支持单向数据流模型,将 redux/flux 风格的状态管理模式扩展到客户端之外,也将服务器包含在内:
通过即时内部循环的乐观状态,随着时间的推移被较慢的外部循环(持久化到服务器并将更新的服务器状态同步回集合)所取代。
@tanstack/db-collections 中实现了许多内置集合类型:
你也可以使用:
所有集合都可选地支持 schema。
如果提供,则必须是符合标准模式的模式实例,例如 Zod 或 Effect 模式。
集合将使用该模式作为其类型,因此如果提供模式,则不能同时传入显式类型(例如 createCollection<Todo>())。
TanStack Query 使用托管查询获取数据。使用 queryCollectionOptions 通过 TanStack Query 将数据提取到集合中:
const todoCollection = createCollection(queryCollectionOptions({
queryKey: ['todoItems'],
queryFn: async () => fetch('/api/todos'),
getKey: (item) => item.id,
schema: todoSchema // 任何标准模式
}))
const todoCollection = createCollection(queryCollectionOptions({
queryKey: ['todoItems'],
queryFn: async () => fetch('/api/todos'),
getKey: (item) => item.id,
schema: todoSchema // 任何标准模式
}))
集合将填充查询结果。
Electric 是一个用于 Postgres 的读路径同步引擎。它允许你将 Postgres 数据库中的数据子集同步到 TanStack DB 集合中,通过你的 API。
Electric 的主要同步原语是 Shape。使用 electricCollectionOptions 将形状同步到集合中:
import { createCollection } from '@tanstack/react-db'
import { electricCollectionOptions } from '@tanstack/db-collections'
export const todoCollection = createCollection(electricCollectionOptions({
id: 'todos',
shapeOptions: {
url: 'https://example.com/v1/shape',
params: {
table: 'todos'
}
},
getKey: (item) => item.id,
schema: todoSchema
}))
import { createCollection } from '@tanstack/react-db'
import { electricCollectionOptions } from '@tanstack/db-collections'
export const todoCollection = createCollection(electricCollectionOptions({
id: 'todos',
shapeOptions: {
url: 'https://example.com/v1/shape',
params: {
table: 'todos'
}
},
getKey: (item) => item.id,
schema: todoSchema
}))
Electric 集合需要两个 Electric 特定的选项:
创建集合时,同步会自动开始。
Electric 形状允许你使用 where 子句过滤数据:
export const myPendingTodos = createCollection(electricCollectionOptions({
id: 'todos',
shapeOptions: {
url: 'https://example.com/v1/shape',
params: {
table: 'todos',
where: `
status = 'pending'
AND
user_id = '${user.id}'
`
}
},
getKey: (item) => item.id,
schema: todoSchema
}))
export const myPendingTodos = createCollection(electricCollectionOptions({
id: 'todos',
shapeOptions: {
url: 'https://example.com/v1/shape',
params: {
table: 'todos',
where: `
status = 'pending'
AND
user_id = '${user.id}'
`
}
},
getKey: (item) => item.id,
schema: todoSchema
}))
Tip
用于过滤同步到 ElectricCollection 中的数据的形状 where 子句与用于在组件中查询数据的实时查询不同。
实时查询比形状更具表现力,允许你跨集合查询、连接、聚合等。形状仅包含已过滤的数据库表,并用于填充集合中的数据。
如果你需要对同步到集合中的数据进行更多控制,Electric 允许你使用你的 API 作为代理来授权和过滤数据。
有关更多信息,请参阅 Electric 文档。
localStorage 集合存储少量本地状态,这些状态在浏览器会话之间持久存在,并在浏览器选项卡之间实时同步。所有数据都存储在单个 localStorage 键下,并使用存储事件自动同步。
使用 localStorageCollectionOptions 创建一个将数据存储在 localStorage 中的集合:
import { createCollection } from '@tanstack/react-db'
import { localStorageCollectionOptions } from '@tanstack/db-collections'
export const userPreferencesCollection = createCollection(localStorageCollectionOptions({
id: 'user-preferences',
storageKey: 'app-user-prefs', // localStorage 键
getKey: (item) => item.id,
schema: userPrefsSchema
}))
import { createCollection } from '@tanstack/react-db'
import { localStorageCollectionOptions } from '@tanstack/db-collections'
export const userPreferencesCollection = createCollection(localStorageCollectionOptions({
id: 'user-preferences',
storageKey: 'app-user-prefs', // localStorage 键
getKey: (item) => item.id,
schema: userPrefsSchema
}))
localStorage 集合需要:
突变处理程序(onInsert、onUpdate、onDelete)完全是可选的。无论你是否提供处理程序,数据都将持久保存到 localStorage。你可以提供备用存储后端,如 sessionStorage 或与 localStorage API 匹配的自定义实现。
export const sessionCollection = createCollection(localStorageCollectionOptions({
id: 'session-data',
storageKey: 'session-key',
storage: sessionStorage, // 改用 sessionStorage
getKey: (item) => item.id
}))
export const sessionCollection = createCollection(localStorageCollectionOptions({
id: 'session-data',
storageKey: 'session-key',
storage: sessionStorage, // 改用 sessionStorage
getKey: (item) => item.id
}))
Tip
localStorage 集合非常适合用户偏好、UI 状态以及其他应在本地持久化但不需要服务器同步的数据。对于服务器同步的数据,请改用 QueryCollection 或 ElectricCollection。
LocalOnly 集合专为不需要跨浏览器会话持久化或跨选项卡同步的内存中客户端数据或 UI 状态而设计。它们提供了一种简单的方法来管理临时的、仅会话的数据,并提供完整的乐观突变支持。
使用 localOnlyCollectionOptions 创建一个仅在内存中存储数据的集合:
import { createCollection } from '@tanstack/react-db'
import { localOnlyCollectionOptions } from '@tanstack/db-collections'
export const uiStateCollection = createCollection(localOnlyCollectionOptions({
id: 'ui-state',
getKey: (item) => item.id,
schema: uiStateSchema,
// 可选的初始数据以填充集合
initialData: [
{ id: 'sidebar', isOpen: false },
{ id: 'theme', mode: 'light' }
]
}))
import { createCollection } from '@tanstack/react-db'
import { localOnlyCollectionOptions } from '@tanstack/db-collections'
export const uiStateCollection = createCollection(localOnlyCollectionOptions({
id: 'ui-state',
getKey: (item) => item.id,
schema: uiStateSchema,
// 可选的初始数据以填充集合
initialData: [
{ id: 'sidebar', isOpen: false },
{ id: 'theme', mode: 'light' }
]
}))
LocalOnly 集合需要:
可选配置:
突变处理程序完全是可选的。提供时,它们会在确认乐观状态之前被调用。集合会自动管理从乐观状态到确认状态的转换。
export const tempDataCollection = createCollection(localOnlyCollectionOptions({
id: 'temp-data',
getKey: (item) => item.id,
onInsert: async ({ transaction }) => {
// 确认插入前的自定义逻辑
console.log('正在插入:', transaction.mutations[0].modified)
},
onUpdate: async ({ transaction }) => {
// 确认更新前的自定义逻辑
const { original, modified } = transaction.mutations[0]
console.log('从', original, '更新到', modified)
}
}))
export const tempDataCollection = createCollection(localOnlyCollectionOptions({
id: 'temp-data',
getKey: (item) => item.id,
onInsert: async ({ transaction }) => {
// 确认插入前的自定义逻辑
console.log('正在插入:', transaction.mutations[0].modified)
},
onUpdate: async ({ transaction }) => {
// 确认更新前的自定义逻辑
const { original, modified } = transaction.mutations[0]
console.log('从', original, '更新到', modified)
}
}))
Tip
LocalOnly 集合非常适合临时 UI 状态、表单数据或任何不需要持久化的客户端数据。对于应跨会话持久化的数据,请改用 LocalStorageCollection。
实时查询返回集合。这允许你从其他集合派生集合。
例如:
import { createLiveQueryCollection, eq } from "@tanstack/db"
// 假设你有一个待办事项集合。
const todoCollection = createCollection({
// 配置
})
// 你可以派生一个新集合,它是它的子集。
const completedTodoCollection = createLiveQueryCollection({
startSync: true,
query: (q) =>
q
.from({ todo: todoCollection })
.where(({ todo }) => todo.completed)
})
import { createLiveQueryCollection, eq } from "@tanstack/db"
// 假设你有一个待办事项集合。
const todoCollection = createCollection({
// 配置
})
// 你可以派生一个新集合,它是它的子集。
const completedTodoCollection = createLiveQueryCollection({
startSync: true,
query: (q) =>
q
.from({ todo: todoCollection })
.where(({ todo }) => todo.completed)
})
这也适用于连接以从多个源集合派生集合。并且它是递归工作的——你可以从其他派生集合派生集合。更改使用差分数据流有效地传播,并且一直都是集合。
在 ../packages/db/src/collection.ts 中有一个基本的 Collection 类。你可以直接使用它,也可以将其作为实现自定义集合类型的基类。
有关参考,请参阅 ../packages/db-collections 中的现有实现。
使用 useLiveQuery 钩子将实时查询结果分配给 React 组件中的状态变量:
import { useLiveQuery } from '@tanstack/react-db'
import { eq } from '@tanstack/db'
const Todos = () => {
const { data: todos } = useLiveQuery((q) =>
q
.from({ todo: todoCollection })
.where(({ todo }) => eq(todo.completed, false))
.orderBy(({ todo }) => todo.created_at, 'asc')
.select(({ todo }) => ({
id: todo.id,
text: todo.text
}))
)
return <List items={ todos } />
}
import { useLiveQuery } from '@tanstack/react-db'
import { eq } from '@tanstack/db'
const Todos = () => {
const { data: todos } = useLiveQuery((q) =>
q
.from({ todo: todoCollection })
.where(({ todo }) => eq(todo.completed, false))
.orderBy(({ todo }) => todo.created_at, 'asc')
.select(({ todo }) => ({
id: todo.id,
text: todo.text
}))
)
return <List items={ todos } />
}
你还可以使用连接跨集合查询:
import { useLiveQuery } from '@tanstack/react-db'
import { eq } from '@tanstack/db'
const Todos = () => {
const { data: todos } = useLiveQuery((q) =>
q
.from({ todos: todoCollection })
.join(
{ lists: listCollection },
({ todos, lists }) => eq(lists.id, todos.listId),
'inner'
)
.where(({ lists }) => eq(lists.active, true))
.select(({ todos, lists }) => ({
id: todos.id,
title: todos.title,
listName: lists.name
}))
)
return <List items={ todos } />
}
import { useLiveQuery } from '@tanstack/react-db'
import { eq } from '@tanstack/db'
const Todos = () => {
const { data: todos } = useLiveQuery((q) =>
q
.from({ todos: todoCollection })
.join(
{ lists: listCollection },
({ todos, lists }) => eq(lists.id, todos.listId),
'inner'
)
.where(({ lists }) => eq(lists.active, true))
.select(({ todos, lists }) => ({
id: todos.id,
title: todos.title,
listName: lists.name
}))
)
return <List items={ todos } />
}
你还可以使用底层的 queryBuilder API 直接构建查询(在组件生命周期之外):
import { createLiveQueryCollection, eq } from "@tanstack/db"
const completedTodos = createLiveQueryCollection({
startSync: true,
query: (q) =>
q.from({ todo: todoCollection })
.where(({ todo }) => eq(todo.completed, true))
})
const results = completedTodos.toArray
import { createLiveQueryCollection, eq } from "@tanstack/db"
const completedTodos = createLiveQueryCollection({
startSync: true,
query: (q) =>
q.from({ todo: todoCollection })
.where(({ todo }) => eq(todo.completed, true))
})
const results = completedTodos.toArray
另请注意:
有关更多用法示例,请参阅 query-builder 测试。
事务性突变器允许你跨集合批处理和暂存本地更改,并具有:
突变器是使用 mutationFn 创建的。你可以为整个应用程序定义一个通用的 mutationFn。或者你可以定义特定于集合或突变的函数。
mutationFn 负责处理本地更改并对其进行处理,通常是将它们发送到服务器或数据库进行存储,例如:
import type { MutationFn } from '@tanstack/react-db'
const mutationFn: MutationFn = async ({ transaction }) => {
const response = await api.todos.create(transaction.mutations)
if (!response.ok) {
// 引发错误将回滚乐观状态。
throw new Error(`HTTP 错误:${response.status}`)
}
const result = await response.json()
// 等待事务从服务器同步回来
// 然后再丢弃乐观状态。
const collection: Collection = transaction.mutations[0].collection
await collection.refetch()
}
import type { MutationFn } from '@tanstack/react-db'
const mutationFn: MutationFn = async ({ transaction }) => {
const response = await api.todos.create(transaction.mutations)
if (!response.ok) {
// 引发错误将回滚乐观状态。
throw new Error(`HTTP 错误:${response.status}`)
}
const result = await response.json()
// 等待事务从服务器同步回来
// 然后再丢弃乐观状态。
const collection: Collection = transaction.mutations[0].collection
await collection.refetch()
}
将 createOptimisticAction 与你的 mutationFn 和 onMutate 函数一起使用,以创建一个可以在组件中以完全自定义的方式改变数据的操作:
import { createOptimisticAction } from '@tanstack/react-db'
// 创建 `addTodo` 操作,传入你的 `mutationFn` 和 `onMutate`。
const addTodo = createOptimisticAction<string>({
onMutate: (text) => {
// 立即应用本地乐观状态。
todoCollection.insert({
id: uuid(),
text,
completed: false
})
},
mutationFn: async (text) => {
// 将待办事项持久化到你的后端
const response = await fetch('/api/todos', {
method: 'POST',
body: JSON.stringify({ text, completed: false }),
})
return response.json()
}
})
const Todo = () => {
const handleClick = () => {
// 触发 onMutate,然后触发 mutationFn
addTodo('🔥 让应用更快')
}
return <Button onClick={ handleClick } />
}
import { createOptimisticAction } from '@tanstack/react-db'
// 创建 `addTodo` 操作,传入你的 `mutationFn` 和 `onMutate`。
const addTodo = createOptimisticAction<string>({
onMutate: (text) => {
// 立即应用本地乐观状态。
todoCollection.insert({
id: uuid(),
text,
completed: false
})
},
mutationFn: async (text) => {
// 将待办事项持久化到你的后端
const response = await fetch('/api/todos', {
method: 'POST',
body: JSON.stringify({ text, completed: false }),
})
return response.json()
}
})
const Todo = () => {
const handleClick = () => {
// 触发 onMutate,然后触发 mutationFn
addTodo('🔥 让应用更快')
}
return <Button onClick={ handleClick } />
}
通过手动创建事务,你可以完全控制它们的生命周期和行为。createOptimisticAction 是一个约 25 行的函数,它实现了一个常见的事务模式。随意发明你自己的模式!
以下是你可能使用事务的一种方式。
import { createTransaction } from "@tanstack/react-db"
const addTodoTx = createTransaction({
autoCommit: false,
mutationFn: async ({ transaction }) => {
// 将数据持久化到后端
await Promise.all(transaction.mutations.map(mutation => {
return await api.saveTodo(mutation.modified)
})
},
})
// 应用第一个更改
addTodoTx.mutate(() => todoCollection.insert({ id: '1', text: '第一个待办事项', completed: false }))
// 用户审查更改
// 应用另一个更改
addTodoTx.mutate(() => todoCollection.insert({ id: '2', text: '第二个待办事项', completed: false }))
// 用户决定保存,我们调用 .commit(),突变将持久化到后端。
addTodoTx.commit()
import { createTransaction } from "@tanstack/react-db"
const addTodoTx = createTransaction({
autoCommit: false,
mutationFn: async ({ transaction }) => {
// 将数据持久化到后端
await Promise.all(transaction.mutations.map(mutation => {
return await api.saveTodo(mutation.modified)
})
},
})
// 应用第一个更改
addTodoTx.mutate(() => todoCollection.insert({ id: '1', text: '第一个待办事项', completed: false }))
// 用户审查更改
// 应用另一个更改
addTodoTx.mutate(() => todoCollection.insert({ id: '2', text: '第二个待办事项', completed: false }))
// 用户决定保存,我们调用 .commit(),突变将持久化到后端。
addTodoTx.commit()
事务会经历以下状态:
集合支持 insert、update 和 delete 操作。
// 插入单个项目
myCollection.insert({ text: "买菜", completed: false })
// 插入多个项目
insert([
{ text: "买菜", completed: false },
{ text: "遛狗", completed: false },
])
// 使用自定义键插入
insert({ text: "买菜" }, { key: "grocery-task" })
// 插入单个项目
myCollection.insert({ text: "买菜", completed: false })
// 插入多个项目
insert([
{ text: "买菜", completed: false },
{ text: "遛狗", completed: false },
])
// 使用自定义键插入
insert({ text: "买菜" }, { key: "grocery-task" })
我们使用代理将更新捕获为不可变的草稿乐观更新。
// 更新单个项目
update(todo.id, (draft) => {
draft.completed = true
})
// 更新多个项目
update([todo1.id, todo2.id], (drafts) => {
drafts.forEach((draft) => {
draft.completed = true
})
})
// 使用元数据更新
update(todo.id, { metadata: { reason: "用户更新" } }, (draft) => {
draft.text = "更新后的文本"
})
// 更新单个项目
update(todo.id, (draft) => {
draft.completed = true
})
// 更新多个项目
update([todo1.id, todo2.id], (drafts) => {
drafts.forEach((draft) => {
draft.completed = true
})
})
// 使用元数据更新
update(todo.id, { metadata: { reason: "用户更新" } }, (draft) => {
draft.text = "更新后的文本"
})
// 删除单个项目
delete(todo.id)
// 删除多个项目
delete([todo1.id, todo2.id])
// 使用元数据删除
delete(todo.id, { metadata: { reason: "已完成" } })
// 删除单个项目
delete(todo.id)
// 删除多个项目
delete([todo1.id, todo2.id])
// 使用元数据删除
delete(todo.id, { metadata: { reason: "已完成" } })
这里我们演示了两种常见的 TanStack DB 使用方式:
Tip
你可以组合这些模式。TanStack DB 的一个好处是你可以将加载数据和处理突变的不同方式集成到同一个应用程序中。你的组件不需要知道数据来自哪里或去向何处。
你可以通过 TanStack Query 将 TanStack DB 与现有的 REST API 一起使用。
步骤如下:
import { useLiveQuery, createCollection } from "@tanstack/react-db"
import { queryCollectionOptions } from "@tanstack/db-collections"
// 使用 TanStack Query 将数据加载到集合中。
// 通常在 `collections` 模块中定义这些。
const todoCollection = createCollection<Todo>(queryCollectionOptions({
queryKey: ["todos"],
queryFn: async () => fetch("/api/todos"),
getKey: (item) => item.id,
schema: todoSchema, // 任何标准模式
onInsert: ({ transaction }) => {
const { changes: newTodo } = transaction.mutations[0]
// 通过将其发送到你的 API 来处理本地写入。
await api.todos.create(newTodo)
}
// 根据需要添加 onUpdate、onDelete。
}))
const listCollection = createCollection<TodoList>(queryCollectionOptions({
queryKey: ["todo-lists"],
queryFn: async () => fetch("/api/todo-lists"),
getKey: (item) => item.id,
schema: todoListSchema
onInsert: ({ transaction }) => {
const { changes: newTodo } = transaction.mutations[0]
// 通过将其发送到你的 API 来处理本地写入。
await api.todoLists.create(newTodo)
}
// 根据需要添加 onUpdate、onDelete。
}))
const Todos = () => {
// 使用实时查询读取数据。这里我们显示一个实时
// 查询,它连接了两个集合。
const { data: todos } = useLiveQuery((q) =>
q
.from({ todo: todoCollection })
.join(
{ list: listCollection },
({ todo, list }) => eq(list.id, todo.list_id),
'inner'
)
.where(({ list }) => eq(list.active, true))
.select(({ todo, list }) => ({
id: todo.id,
text: todo.text,
status: todo.status,
listName: list.name
}))
)
// ...
}
import { useLiveQuery, createCollection } from "@tanstack/react-db"
import { queryCollectionOptions } from "@tanstack/db-collections"
// 使用 TanStack Query 将数据加载到集合中。
// 通常在 `collections` 模块中定义这些。
const todoCollection = createCollection<Todo>(queryCollectionOptions({
queryKey: ["todos"],
queryFn: async () => fetch("/api/todos"),
getKey: (item) => item.id,
schema: todoSchema, // 任何标准模式
onInsert: ({ transaction }) => {
const { changes: newTodo } = transaction.mutations[0]
// 通过将其发送到你的 API 来处理本地写入。
await api.todos.create(newTodo)
}
// 根据需要添加 onUpdate、onDelete。
}))
const listCollection = createCollection<TodoList>(queryCollectionOptions({
queryKey: ["todo-lists"],
queryFn: async () => fetch("/api/todo-lists"),
getKey: (item) => item.id,
schema: todoListSchema
onInsert: ({ transaction }) => {
const { changes: newTodo } = transaction.mutations[0]
// 通过将其发送到你的 API 来处理本地写入。
await api.todoLists.create(newTodo)
}
// 根据需要添加 onUpdate、onDelete。
}))
const Todos = () => {
// 使用实时查询读取数据。这里我们显示一个实时
// 查询,它连接了两个集合。
const { data: todos } = useLiveQuery((q) =>
q
.from({ todo: todoCollection })
.join(
{ list: listCollection },
({ todo, list }) => eq(list.id, todo.list_id),
'inner'
)
.where(({ list }) => eq(list.active, true))
.select(({ todo, list }) => ({
id: todo.id,
text: todo.text,
status: todo.status,
listName: list.name
}))
)
// ...
}
此模式允许你扩展现有的 TanStack Query 应用程序或任何基于 REST API 构建的应用程序,使其具有极快的跨集合实时查询和本地乐观突变,并自动管理乐观状态。
使用 TanStack DB 最强大的方法之一是使用同步引擎,以获得具有实时同步的完全本地优先体验。这允许你将同步逐步引入现有应用程序,同时仍使用现有 API 处理写入。
在这里,我们使用 ElectricSQL 作为同步引擎来说明此模式。
import type { Collection } from '@tanstack/db'
import type { MutationFn, PendingMutation, createCollection } from '@tanstack/react-db'
import { electricCollectionOptions } from '@tanstack/db-collections'
export const todoCollection = createCollection(electricCollectionOptions<Todo>({
id: 'todos',
schema: todoSchema,
// Electric 使用“形状”同步数据。这些是数据库表上的
// 过滤视图,Electric 会为你保持同步。
shapeOptions: {
url: 'https://api.electric-sql.cloud/v1/shape',
params: {
table: 'todos'
}
},
getKey: (item) => item.id,
schema: todoSchema
onInsert: ({ transaction }) => {
const response = await api.todos.create(transaction.mutations[0].modified)
return { txid: response.txid}
}
// 你也可以根据需要实现 onUpdate、onDelete。
}))
const AddTodo = () => {
return (
<Button
onClick={() =>
todoCollection.insert({ text: "🔥 让应用更快" })
}
/>
)
}
import type { Collection } from '@tanstack/db'
import type { MutationFn, PendingMutation, createCollection } from '@tanstack/react-db'
import { electricCollectionOptions } from '@tanstack/db-collections'
export const todoCollection = createCollection(electricCollectionOptions<Todo>({
id: 'todos',
schema: todoSchema,
// Electric 使用“形状”同步数据。这些是数据库表上的
// 过滤视图,Electric 会为你保持同步。
shapeOptions: {
url: 'https://api.electric-sql.cloud/v1/shape',
params: {
table: 'todos'
}
},
getKey: (item) => item.id,
schema: todoSchema
onInsert: ({ transaction }) => {
const response = await api.todos.create(transaction.mutations[0].modified)
return { txid: response.txid}
}
// 你也可以根据需要实现 onUpdate、onDelete。
}))
const AddTodo = () => {
return (
<Button
onClick={() =>
todoCollection.insert({ text: "🔥 让应用更快" })
}
/>
)
}
如果你在使用 TanStack DB 时遇到问题或需要帮助,请在 Discord 上告诉我们或开始 GitHub 讨论: