PGlite React Hooks 实战指南:用 PGliteProvider 与 useLiveQuery 构建响应式 React 应用 📅 发布时间:2026/9/14 22:06:55 👁 浏览次数: PGlite React Hooks 实战指南用 PGliteProvider 与 useLiveQuery 构建响应式 React 应用【免费下载链接】pgliteEmbeddable Postgres with real-time, reactive bindings.项目地址: https://gitcode.com/GitHub_Trending/pg/pglitePGlite 是运行在 WebAssembly 中的可嵌入式 Postgres而electric-sql/pglite-react包则把它的 live query 插件封装成了符合 React 习惯的 Hooks API。本文围绕 pglite-react 包的官方文档 与 framework-hooks/react.md系统讲解PGliteProvider、usePGlite、makePGliteProvider、useLiveQuery、useLiveQuery.sql与useLiveIncrementalQuery六个 API 的完整用法、接口签名、底层实现与测试验证读完即可在 React 项目中实现数据库变化即组件重渲染的响应式数据流。前置条件安装与 live 扩展在 React 项目中使用这些 Hooks 之前需要安装两个包npm install electric-sql/pglite npm install electric-sql/pglite-react其中electric-sql/pglite-react的package.json见 packages/pglite-react/package.json声明了对react的 peer 依赖为^18.0.0 || ^19.0.0 || ^19.0.0-rc即支持 React 18 与 React 19同时依赖electric-sql/pglite提供底层数据库能力。还有一个关键前置所有响应式 Hooks 都构建在 live query 插件之上因此创建 PGlite 实例时必须启用live扩展否则db.live命名空间不存在Hooks 无法工作。创建方式见 live-queries.mdimport { PGlite } from electric-sql/pglite import { live } from electric-sql/pglite/live const db await PGlite.create({ extensions: { live } })PGliteProvider把数据库实例注入组件树PGliteProvider是一个 Provider 组件用于把创建好的 PGlite 实例传递给所有子组件供usePGlite、useLiveQuery、useLiveIncrementalQuery使用。用法是把它包裹在应用根部并通过db属性传入实例import { PGlite } from electric-sql/pglite import { live } from electric-sql/pglite/live import { PGliteProvider } from electric-sql/pglite-react const db await PGlite.create({ extensions: { live } }) const App () { // ... return ( PGliteProvider db{db} {/* 子组件 */} /PGliteProvider ) }从源码看packages/pglite-react/src/provider.tsxPGliteProvider本质上是makePGliteProvider以默认类型PGliteWithLive调用生成的实例它内部通过createContextT | undefined(undefined)创建上下文PGliteProvider组件则渲染ctx.Provider value{db}{children}/ctx.Provider。也就是说Provider 本身不创建数据库只负责传递数据库的初始化与生命周期仍由你控制。usePGlite在任意子组件中获取数据库实例usePGlite用于在组件内取回 Provider 提供的 PGlite 实例之后就可以直接对它执行任意 SQL 操作import { usePGlite } from electric-sql/pglite-react const MyComponent () { const db usePGlite() const insertItem () { db.query(INSERT INTO my_table (name, number) VALUES (Arthur, 42);) } return ( button onClick{insertItem}Insert/button / ) }usePGlite的源码行为值得注意packages/pglite-react/src/provider.tsx它优先返回组件树中上下文里保存的实例如果显式传入一个db参数usePGlite(db)会直接返回该参数而忽略上下文——这为脱离 Provider 的测试或特殊场景提供了逃生通道如果既没有上下文实例也没有显式参数会抛出错误No PGlite instance found, use PGliteProvider to provide one。这提醒我们忘记包裹PGliteProvider是这类 Hooks 最常见的运行时错误来源。对应测试见 packages/pglite-react/test/provider.test.tsxcan receive PGlite用例验证了在PGliteProvider包裹下usePGlite()能取回同一个db引用。makePGliteProvider为扩展类型创建强类型 ProviderPGlite 支持扩展机制扩展会在实例上挂载额外的命名空间与类型例如 live 的db.live、pgvector 的db.vector。默认导出的PGliteProvider只具备PGliteWithLive类型若要同时使用多个扩展并保持类型推断就需要makePGliteProvider。makePGliteProviderT()返回一个带有指定泛型类型T的PGliteProvider组件与usePGliteHook 对。典型用法是在项目里创建一个导出模块import { PGlite, PGliteInterfaceExtensions } from electric-sql/pglite import { LiveNamespace } from electric-sql/pglite/live import { VectorNamespace } from electric-sql/pglite-pgvector import { makePGliteProvider } from electric-sql/pglite-react const { PGliteProvider, usePGlite } makePGliteProvider PGlite PGliteInterfaceExtensions{ live: typeof live vector: typeof vector } () export { PGliteProvider, usePGlite }此后项目中导入的usePGlite()返回的db就同时携带live与vector命名空间及其完整类型不再需要手动断言。需要说明的是LiveNamespace、VectorNamespace等扩展命名空间类型均从对应扩展包导出如electric-sql/pglite/live、electric-sql/pglite-pgvector示例中live、vector即对应扩展模块的值可按实际安装的扩展调整泛型参数。从 provider.tsx 的实现看makePGliteProvider每次调用都会创建独立的 Context因此多个 Provider 体系可以并存而互不干扰。测试can receive PGlite with typed providerprovider.test.tsx验证了makePGliteProviderPGliteWithLive()产出的类型化 Provider/Hook 同样能正确回传实例。useLiveQuery让组件随查询结果自动重渲染useLiveQuery是面向 React 的响应式查询 Hook它包装了 live 扩展的.live.query()API当查询所依赖的表发生变化时组件会自动重新渲染并拿到最新结果。其接口签名如下function useLiveQueryT { [key: string]: unknown }( query: string, params: unknown[] | undefined | null, ): ResultsT两个参数分别是SQL 查询字符串查询的可选参数对应 SQL 中的$1、$2占位符不需要时传null或省略。import { useLiveQuery } from electric-sql/pglite-react const MyComponent () { const maxNumber 100 const items useLiveQuery( SELECT * FROM my_table WHERE number $1 ORDER BY number; , [maxNumber]) return ( { items.map((item) MyItem item{item} / ) } / ) }useLiveQuery 的重载能力直接接收 LiveQuery 对象或 Promise文档描述的接口仅覆盖字符串形式但源码packages/pglite-react/src/hooks.ts揭示了更丰富的重载useLiveQuery除了字符串查询还可以接收一个已经创建好的LiveQueryT对象来自db.live.query(...)的返回值一个PromiseLiveQueryT即未 await 的db.live.query(...)调用本身。对应测试位于 packages/pglite-react/test/hooks.test.tsx 的can take a live query return value directly与can take a live query returned promise directly用例两者都能在后续对表执行 INSERT 后自动把最新行反映到 Hook 返回值中。这三种形态统一由内部实现useLiveQueryImpl处理详见下文底层实现。useLiveQuery.sql标签模板语法构造查询useLiveQuery.sql是useLiveQuery的标签模板函数tagged template形态与 PGlite 核心的 模板查询 API 一一对应。它允许把参数直接内插进模板字符串由底层负责解析成 SQL 与参数数组无需手动维护$1占位符import { useLiveQuery } from electric-sql/pglite-react const MyComponent () { const maxNumber 100 const items useLiveQuery.sql SELECT * FROM my_table WHERE number ${maxNumber} ORDER BY number; // ... }源码hooks.ts显示其实现借助了electric-sql/pglite/template的query as buildQuery函数把模板字符串和插值展开为{ query, params }再交给useLiveQueryImpl。测试updates when query parameter changeshooks.test.tsx验证了当模板内插值从test1变为test2并触发重渲染后查询结果会随之更新为对应行的数据。useLiveIncrementalQuery把 diff 计算下沉到 PostgresuseLiveIncrementalQuery同样提供响应式重渲染但它包装的是.live.incrementalQuery()API。二者的核心差异在于 diff 的承担位置useLiveQuerylive.query在表变化时于 WASM 内部重跑整个查询useLiveIncrementalQuerylive.incrementalQuery在 Postgres 内部维护一张上一状态的临时表表变化后重跑查询并与上次状态做 diff只把变化的部分从 WASM 拷贝到 JS。这在结果集大、行宽wide rows的场景下性能更好尤其适合喂给 React 做渲染。其接口签名function useLiveIncrementalQueryT { [key: string]: unknown }( query: string, params: unknown[] | undefined | null, key: string, ): ResultsT三个参数分别是SQL 查询字符串查询的可选参数diff 算法所依据的键列名key column通常是主键。import { useLiveIncrementalQuery } from electric-sql/pglite-react const MyComponent () { const maxNumber 100 const items useLiveIncrementalQuery( SELECT * FROM my_table WHERE number $1 ORDER BY number; , [maxNumber], id) return ( { items.map((item) MyItem item{item} / ) } / ) }关于key参数对 diff 的意义可参考 live/interface.ts 中LiveIncrementalQueryOptions的定义{ query, params, key, callback, signal }。底层live.changes依赖该键比对行差异并在ChangeUpdate/ChangeInsert中通过__after__字段记录该行应排在哪个 key 之后从而支持有序结果集内的高效移动详见 live-queries.md 中对Change类型的说明。返回值结构Results 与 LiveQueryResults两个 live Hooks 的返回值都是查询结果对象。源码hooks.ts对原始LiveQueryResultsT做了裁剪返回结构为{ rows: T[] // 当前结果行 fields: { name: string; dataTypeID: number }[] // 字段元信息 totalCount?: number // 总行数窗口化查询时存在 offset?: number // 当前偏移窗口化查询时存在 limit?: number // 当前限制窗口化查询时存在 }注意它不包含affectedRows字段。测试can receive initial resultshooks.test.tsx展示了返回值的精确形态例如fields会携带每个列的name与dataTypeID如id列对应类型 OID 23name文本列对应 25。此外需要留意一个异步细节初始渲染时返回值可能是undefined直到 live 查询建立并返回初始结果后才有值。测试中普遍使用waitFor(() expect(result.current).not.toBe(undefined))等待结果就绪实际组件里也应做好空值防御例如items?.rows或可选链。底层实现useLiveQueryImpl 如何做到响应式把文档抽象成机制核心都收敛在 packages/pglite-react/src/hooks.ts 的useLiveQueryImpl中它同时服务于三个公开 HookuseLiveQuery字符串形式、useLiveQuery.sql、useLiveIncrementalQuery。其关键机制可以归纳为四点1. 订阅与清理useEffectHook 在useEffect中根据输入形态建立订阅字符串查询 key未定义 →db.live.queryT(query, currentParams, cb)字符串查询 key已定义 →db.live.incrementalQueryT(query, currentParams, key, cb)PromiseLiveQueryT→ 在.then中保存 liveQuery、用initialResults初始化 state 并subscribe(cb)直接传入LiveQueryT→ 同样用initialResults初始化并订阅。effect 的清理函数会cancelled true并调用unsubscribe()/liveQuery.unsubscribe(cb)确保组件卸载或依赖变化时及时断开订阅避免内存泄漏与对已卸载组件的 setState。2. 参数变化的浅比较paramsEqual使用Object.is逐元素比较新旧参数数组长度不同或任一元素不同都视为变化从而触发重新订阅。测试updates when query parameters changehooks.test.tsx验证了参数值变化与参数个数变化[test1]→[test1,test2]两条路径都会让结果正确刷新。3. 查询语句变化的响应effect 的依赖数组包含query因此 SQL 字符串变化时如测试中SELECT * FROM test改为带 WHERE 的版本会重建订阅useLiveQuery.sql通过buildQuery生成的新{ query, params }同样走这条路径。4. 订阅回调驱动重渲染live 查询的回调cb每次收到新结果即setResults(results)触发 React 重渲染返回时再按上文结构裁剪字段。整个链路实现了Postgres 表数据变化 → WASM 内 live 机制检测 → 回调 → setState → 组件重渲染的响应式闭环。从 live 插件看 Hooks 的能力边界useLiveQuery/useLiveIncrementalQuery的能力上限由 live 扩展决定。参考 live/interface.ts 与 live-queries.md 可知底层LiveNamespace提供三类 APIlive.query()适合小结果集、窄行的基础实时查询PGlite 内机制开销更小live.incrementalQuery()从live.changes发出的增量变化物化完整结果集适合大结果集与宽行也是 React Hooks 的推荐选择live.changes()更低层的变更流 API直接输出INSERT/UPDATE/DELETE变更带__op__、__changed_columns__、__after__等字段可用于实现高效的就地 DOM 更新。在 React Hooks 语境下如果组件需要实现窗口化分页可以直接通过db.live.query({ query, offset, limit, callback })创建带totalCount/offset/limit的窗口查询再把返回的LiveQuery对象或 Promise交给useLiveQuery消费——这正是前面提到的重载形态的典型应用场景。总结一套 API 打通 React 与嵌入式 Postgreselectric-sql/pglite-react提供了从实例注入到响应式查询的完整闭环API作用关键参数PGliteProvider向组件树注入 PGlite 实例dbusePGlite取回上下文中的实例可选db覆盖上下文makePGliteProviderT()生成带扩展类型的 Provider/Hook 对泛型TuseLiveQuery响应式查询包装live.querySQL、可选参数useLiveQuery.sql模板字符串形态的响应式查询内插参数useLiveIncrementalQuery增量 diff 的响应式查询包装live.incrementalQuerySQL、参数、key 列名实战落地只需四步启用live扩展创建 PGlite → 用PGliteProvider包裹应用 → 在子组件用usePGlite或扩展类型化的makePGliteProvider获取实例 → 用useLiveQuery/useLiveIncrementalQuery声明式订阅查询。搭配 React 框架 Hooks 文档、live 查询扩展文档 与 测试用例即可在浏览器中构建数据与界面实时同步的 React 应用。【免费下载链接】pgliteEmbeddable Postgres with real-time, reactive bindings.项目地址: https://gitcode.com/GitHub_Trending/pg/pglite创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考