Get started with Dexie in React
在 React 中开始使用 Dexie
Dexie v3.2 and later comes with reactivity built-in.
Dexie v3.2 及更高版本内置了响应式功能。
In version 3.2 we’ve introduced live queries - queries that observe the result and make your component mirror the data in real time.
If a change is made (by the app itself or from an external tab or worker), a binary range tree algorithm will efficiently detect whether those changes would affect your queries and if so, re-execute your callback and re-render component.
Here’s a simple ToDo app example that demonstrates it.
在 3.2 版本中,我们引入了实时查询功能——这些查询能够观察结果,并使您的组件实时反映数据变化。如果数据发生更改(无论是由应用本身还是从外部标签页或工作线程进行的),二叉范围树算法将高效地检测这些更改是否会影响您的查询,如果影响,则重新执行回调函数并重新渲染组件。以下是一个简单的待办事项应用示例,展示了这一功能。
useLiveQuery() can be explained like this: It observes the result of a promise-returning function that queries Dexie (In contrast to just execute it imperatively).
It is highly composable as you can call other functions that queries dexie and compute a result based on their outcome.
Maybe you already have some functions you wrote long time ago.
Calling them from within the scope of the callback passed to useLiveQuery() will turn your imperative async functions into an observable query.
useLiveQuery() 可以这样解释:它观察一个返回承诺的函数查询 Dexie 的结果(与仅仅命令式地执行它相反)。由于你可以调用其他查询 Dexie 的函数并基于它们的结果进行计算,因此它具有高度的可组合性。或许你早已编写了一些函数。在传递给 useLiveQuery() 的回调函数作用域内调用这些函数,就能将你的命令式异步函数转变为可观察的查询。
1. Create a React project
1. 创建一个 React 项目
Here we refer to React’s own Getting Started page.
这里我们参考 React 官方的入门页面。
For the impatient one, use CodeSandbox to create a React app and start code editing in your browser.
对于急于上手的人,可以使用 CodeSandbox 创建一个 React 应用,并在浏览器中开始代码编辑。
2. Install dependencies 2. 安装依赖项
yarn 纱线
yarn add dexie
yarn add dexie-react-hooks
npm
npm install dexie
npm install dexie-react-hooks
CodeSandbox 代码沙盒
3. Create a file db.js
(or db.ts
)
3. 创建一个文件 db.js
(或 db.ts
)
Applications typically have one single Dexie instance declared as its own module. This is where you declare which tables you need and how each table shall be indexed. A Dexie instance is a singleton throughout the application - you do not need to create it on demand. Export the resulting db
instance from your module so that components or other modules can use it to query or write to the database.
应用程序通常会声明一个单一的 Dexie 实例作为其自身的模块。在这里,您声明所需的数据表以及每个表的索引方式。Dexie 实例在整个应用程序中是单例的——您无需按需创建它。将生成的 db
实例从模块中导出,以便组件或其他模块可以使用它来查询或写入数据库。
// db.js
import Dexie from 'dexie';
export const db = new Dexie('myDatabase');
db.version(1).stores({
friends: '++id, name, age' // Primary key and indexed props
});
Using Typescript? 使用 TypeScript?
If you use Typescript, table properties (such as db.friends
) needs to be explicitly declared on a subclass of Dexie just to help out with the typings for your db instance, its tables and entity models.
如果你使用 TypeScript,表属性(如 db.friends
)需要在 Dexie 的子类上显式声明,以便为你的数据库实例、表和实体模型提供类型支持。
// db.ts
import Dexie, { type EntityTable } from 'dexie';
interface Friend {
id: number;
name: string;
age: number;
}
const db = new Dexie('FriendsDatabase') as Dexie & {
friends: EntityTable<
Friend,
'id' // primary key "id" (for the typings only)
>;
};
// Schema declaration:
db.version(1).stores({
friends: '++id, name, age' // primary key "id" (for the runtime!)
});
export type { Friend };
export { db };
4. Create a component that adds some data
Writing to the database can be done using Table.add(), Table.put(), Table.update() and Collection.modify() - see Dexie’s quick reference for examples. Here we’re gonna create a simple React component that allows the user to add friends into the database using Table.add().
export function AddFriendForm({ defaultAge } = { defaultAge: 21 }) {
const [name, setName] = useState('');
const [age, setAge] = useState(defaultAge);
const [status, setStatus] = useState('');
async function addFriend() {
try {
// Add the new friend!
const id = await db.friends.add({
name,
age
});
setStatus(`Friend ${name} successfully added. Got id ${id}`);
setName('');
setAge(defaultAge);
} catch (error) {
setStatus(`Failed to add ${name}: ${error}`);
}
}
return (
<>
<p>{status}</p>
Name:
<input
type="text"
value={name}
onChange={(ev) => setName(ev.target.value)}
/>
Age:
<input
type="number"
value={age}
onChange={(ev) => setAge(Number(ev.target.value))}
/>
<button onClick={addFriend}>Add</button>
</>
);
}
5. Create a component that queries data
Write a simple component that just renders all friends in the database.
export function FriendList() {
const friends = useLiveQuery(() => db.friends.toArray());
return (
<ul>
{friends?.map((friend) => (
<li key={friend.id}>
{friend.name}, {friend.age}
</li>
))}
</ul>
);
}
To make more detailed queries, refer to Dexie’s quick reference for querying items.
Notice two things here:
- The function passed to useLiveQuery() queries dexie for all friends.
- The result will be undefined momentarily before the very initial result arrives - which explains why we refer it as
friends?
rather thanfriends
.
6. Pass some query params
Let’s improve the FriendList component and allow a parent component to pass some props that we use from within the query. This time let’s also use async / await (for pedagogical reasons only - it makes it simple to extend the function to do more queries if needed).
export function FriendList({ minAge, maxAge }) {
const friends = useLiveQuery(
async () => {
//
// Query Dexie's API
//
const friends = await db.friends
.where('age')
.between(minAge, maxAge)
.toArray();
// Return result
return friends;
},
// specify vars that affect query:
[minAge, maxAge]
);
return (
<ul>
{friends?.map((friend) => (
<li key={friend.id}>
{friend.name}, {friend.age}
</li>
))}
</ul>
);
}
Notice two things in the above example:
- We pass two arguments to useLiveQuery(): the async function and the deps. The callback is just a plain async function that can compute any type of result based on what it queries. It can use Promise.all() to query things in parallel or query things sequentially after each other. Any Dexie-call along the way will be marked for observation. In any case the end result will become observed.
- Deps are needed when the querying function uses closures that affect the query. In this case the minAge and maxAge parameters. A parent component may pass new values for it and that needs to be detected and make our query reexecuted.
7. Put it together
export const App = () => (
<>
<h1>My simple Dexie app</h1>
<h2>Add Friend</h2>
<AddFriendForm defaultAge={21} />
<h2>Friend List</h2>
<FriendList minAge={18} maxAge={65} />
</>
);
When running this example, notice that adding friends within the given age range will make them show up instantly in your view.
Things to play with
Test out how to edit query parameters and watch the live results update, or open up app in several windows and see them instantly reflect the changes from the other window…
Make query parameters editable
Add a new component that allows the user to specify minAge
and maxAge
and pass those into the props to <FriendList>
. You will notice that updating the props will also be instantly reflected in the query results of your app.
Run app in multiple windows
Open your app (or for example this one) in multiple new windows and watch them react to each other’s changes.
NOTE: IndexedDB is tied to using same browser and same origin. Sync across different origins, browsers, clients and users is another topic and requires a sync solution. If you’re interested, have a look at what’s coming in Dexie Cloud.
Observe joined data
Do something similar to this sample and observe the result of a function similar to getBandsStartingWithA()
(a function that compose a result from multiple related queries). Notice that any change that affects any of the queries will make the component rerender, including the related data.
More Samples and Resources
Table of Contents
- API Reference API 参考
-
Access Control in Dexie Cloud
Dexie Cloud 中的访问控制 -
Add demo users
添加演示用户 -
Add public data
添加公共数据 -
Authentication in Dexie Cloud
Dexie Cloud 中的身份验证 - Best Practices 最佳实践
- Building Addons 构建附加组件
- Collection 收藏集
- Collection.and()
- Collection.clone()
- Collection.count()
- Collection.delete()
- Collection.desc()
- Collection.distinct()
- Collection.each()
- Collection.eachKey()
- Collection.eachPrimaryKey()
- Collection.eachUniqueKey()
- Collection.filter()
- Collection.first()
- Collection.keys()
- Collection.last()
- Collection.limit()
- Collection.modify()
- Collection.offset()
- Collection.or()
- Collection.primaryKeys()
- Collection.raw()
- Collection.reverse()
- Collection.sortBy()
- Collection.toArray()
- Collection.uniqueKeys()
- Collection.until()
- Compound Index 复合索引
-
Consistency in Dexie Cloud
Dexie Cloud 中的一致性 -
Consistent add() operator
一致的 add() 运算符 -
Consistent remove() operator
一致的 remove() 操作符 -
Consistent replacePrefix() operator
一致的 replacePrefix() 操作符 -
Consuming Dexie as a module
将 Dexie 作为模块使用 -
Custom Emails in Dexie Cloud
Dexie Cloud 中的自定义电子邮件 - DBCore
- DBCoreAddRequest
- DBCoreCountRequest
- DBCoreCursor
- DBCoreDeleteRangeRequest
- DBCoreDeleteRequest
- DBCoreGetManyRequest
- DBCoreGetRequest
- DBCoreIndex
- DBCoreKeyRange
- DBCoreMutateRequest
- DBCoreMutateResponse
- DBCoreOpenCursorRequest
- DBCorePutRequest
- DBCoreQuery
- DBCoreQueryRequest
- DBCoreQueryResponse DBCore 查询响应
- DBCoreRangeType
- DBCoreSchema
- DBCoreTable
- DBCoreTableSchema
- DBCoreTransaction DBCore 事务
- DBCoreTransactionMode DBCore 事务模式
- DBPermissionSet
- Deprecations 弃用项
- Derived Work 衍生作品
- Design 设计
- Dexie Cloud API
-
Dexie Cloud API Limits
Dexie Cloud API 限制 -
Dexie Cloud Best Practices
Dexie Cloud 最佳实践 - Dexie Cloud CLI
-
Dexie Cloud Docs
Dexie Cloud 文档 - Dexie Cloud REST API
-
Dexie Cloud Web Hooks
Dexie Cloud Web 钩子 - Dexie Constructor Dexie 构造函数
- Dexie.AbortError
- Dexie.BulkError
- Dexie.ConstraintError
- Dexie.DataCloneError
- Dexie.DataError
-
Dexie.DatabaseClosedError
Dexie.数据库已关闭错误 - Dexie.IncompatiblePromiseError
- Dexie.InternalError Dexie.内部错误
-
Dexie.InvalidAccessError
Dexie.无效访问错误 -
Dexie.InvalidArgumentError
Dexie.无效参数错误 -
Dexie.InvalidStateError
Dexie.无效状态错误 -
Dexie.InvalidTableError
Dexie.无效表错误 - Dexie.MissingAPIError
- Dexie.ModifyError
-
Dexie.NoSuchDatabaseErrorError
Dexie.无此数据库错误 - Dexie.NotFoundError
- Dexie.Observable
- Dexie.Observable.DatabaseChange
- Dexie.OpenFailedError
-
Dexie.PrematureCommitError
Dexie.Prem 提交错误 - Dexie.QuotaExceededError
- Dexie.ReadOnlyError Dexie.只读错误
- Dexie.SchemaError
- Dexie.SubTransactionError
- Dexie.Syncable
- Dexie.Syncable.IDatabaseChange
- Dexie.Syncable.IPersistentContext
- Dexie.Syncable.ISyncProtocol
-
Dexie.Syncable.StatusTexts
Dexie.Syncable.状态文本 -
Dexie.Syncable.Statuses
Dexie.Syncable.状态 - Dexie.Syncable.registerSyncProtocol()
- Dexie.TimeoutError
- Dexie.TransactionInactiveError
- Dexie.UnknownError Dexie.未知错误
- Dexie.UnsupportedError Dexie.不支持的错误
- Dexie.UpgradeError()
- Dexie.VersionChangeError
- Dexie.VersionError
- Dexie.[table] Dexie.[表]
- Dexie.addons
- Dexie.async()
- Dexie.backendDB()
- Dexie.close()
-
Dexie.currentTransaction
Dexie.当前事务 - Dexie.debug
- Dexie.deepClone()
- Dexie.defineClass()
- Dexie.delByKeyPath()
- Dexie.delete()
- Dexie.derive()
- Dexie.events()
- Dexie.exists()
- Dexie.extend()
- Dexie.fakeAutoComplete()
- Dexie.getByKeyPath()
- Dexie.getDatabaseNames()
- Dexie.hasFailed()
- Dexie.ignoreTransaction()
- Dexie.isOpen()
- Dexie.js
- Dexie.name
- Dexie.on()
- Dexie.on.blocked
- Dexie.on.close
- Dexie.on.error
- Dexie.on.populate
- Dexie.on.populate-(old-version)
- Dexie.on.ready
- Dexie.on.storagemutated
- Dexie.on.versionchange
- Dexie.open()
- Dexie.override()
- Dexie.semVer
- Dexie.setByKeyPath()
- Dexie.shallowClone()
- Dexie.spawn()
- Dexie.table()
- Dexie.tables
- Dexie.transaction()
- Dexie.transaction()-(old-version)
- Dexie.use()
- Dexie.verno
- Dexie.version
- Dexie.version()
- Dexie.vip()
- Dexie.waitFor()
- DexieCloudOptions
- DexieError
- Docs Home
- Download
- EntityTable
- Export and Import Database
- Get started with Dexie in Angular
- Get started with Dexie in React
- Get started with Dexie in Svelte
- Get started with Dexie in Vue
- Hello World
- How To Use the StorageManager API
- Inbound
- IndexSpec
- Indexable Type
- IndexedDB on Safari
- Invite
- Member
- Migrating existing DB to Dexie
- MultiEntry Index
- PersistedSyncState
- Privacy Policy
- Promise
- Promise.PSD
- Promise.catch()
- Promise.finally()
- Promise.on.error
- Promise.onuncatched
- Questions and Answers
- Realm
- Releasing Dexie
- Road Map
- Road Map: Dexie 5.0
- Road Map: Dexie Cloud
- Role
- Run Dexie Cloud on Own Servers
- Sharding and Scalability
- Simplify with yield
- Support Ukraine
- SyncState
- Table
- Table Schema
- Table.add()
- Table.bulkAdd()
- Table.bulkDelete()
- Table.bulkGet()
- Table.bulkPut()
- Table.bulkUpdate()
- Table.clear()
- Table.count()
- Table.defineClass()
- Table.delete()
- Table.each()
- Table.filter()
- Table.get()
- Table.hook('creating')
- Table.hook('deleting')
- Table.hook('reading')
- Table.hook('updating')
- Table.limit()
- Table.mapToClass()
- Table.name
- Table.offset()
- Table.orderBy()
- Table.put()
- Table.reverse()
- Table.schema
- Table.toArray()
- Table.toCollection()
- Table.update()
- Table.where()
- The main limitations of IndexedDB
- Transaction
- Transaction.abort()
- Transaction.on.abort
- Transaction.on.complete
- Transaction.on.error
- Transaction.table()
- Tutorial
- Typescript
- Typescript (old)
- Understanding the basics
- UserLogin
- Version
- Version.stores()
- Version.upgrade()
- WhereClause
- WhereClause.above()
- WhereClause.aboveOrEqual()
- WhereClause.anyOf()
- WhereClause.anyOfIgnoreCase()
- WhereClause.below()
- WhereClause.belowOrEqual()
- WhereClause.between()
- WhereClause.equals()
- WhereClause.equalsIgnoreCase()
- WhereClause.inAnyRange()
- WhereClause.noneOf()
- WhereClause.notEqual()
- WhereClause.startsWith()
- WhereClause.startsWithAnyOf()
- WhereClause.startsWithAnyOfIgnoreCase()
- WhereClause.startsWithIgnoreCase()
- db.cloud.configure()
- db.cloud.currentUser
- db.cloud.currentUserId
- db.cloud.events.syncComplete
- db.cloud.invites
- db.cloud.login()
- db.cloud.logout()
- db.cloud.options
- db.cloud.permissions()
- db.cloud.persistedSyncState
- db.cloud.roles
- db.cloud.schema
- db.cloud.sync()
- db.cloud.syncState
- db.cloud.userInteraction
- db.cloud.usingServiceWorker
- db.cloud.version
- db.cloud.webSocketStatus
- db.members
- db.realms
- db.roles
- db.syncable.connect()
- db.syncable.delete()
- db.syncable.disconnect()
- db.syncable.getOptions()
- db.syncable.getStatus()
- db.syncable.list()
- db.syncable.on('statusChanged')
- db.syncable.setFilter()
- dexie-cloud-addon
- dexie-react-hooks
- liveQuery()
- unhandledrejection-event
- useLiveQuery()
- useObservable()
- usePermissions()