mcpField
W
watermelondb

watermelondb

github.com/jchaselubitz开发049 次安装37 周前更新
代码仓库

关于 watermelondb

面向使用 WatermelonDB 构建 React 数据库界面的开发者,讲解模型与响应式查询。重点说明观测结果的对象引用如何影响重新渲染,并提供订阅和 `withObservables` 模式。

主要功能

  1. 01用 `findAndObserve` 观测记录
  2. 02订阅查询结果
  3. 03避免 React 状态引用导致跳过渲染
  4. 04用 `withObservables` 注入响应式属性

适用场景

  1. 01记录更新后刷新详情界面
  2. 02根据变化的查询结果渲染列表
  3. 03将观测到的模型属性传入组件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jchaselubitz/drill-app --skill watermelondb

安装后在 Claude Code 中输入/watermelondb确认已加载。

附带文件meta.yaml

详细介绍

WatermelonDB 模型与观察

概述

此技能涵盖 WatermelonDB 模型(database/models/)、观察 (响应式查询),以及确保观察到的数据发生变化时 React 会重新渲染。在使用 findAndObserve、query.observe()、 withObservables,或任何订阅数据库变化的屏幕时,请使用此技能。


观察与 React 重新渲染

findAndObserve 与相同引用的发射
  • findAndObserve(id)(作用于集合):按 ID 获取记录,返回一个 Observable,该 Observable 在订阅时立即发射,且在记录 更新或删除时都会发射。
  • 更新模型时(例如通过 model.update() 或 @writer 方法),Observable 会以更新后的属性发射同一个对象引用,而不会 发射新的模型实例。
React useState 的提前退出
  • useState 使用 Object.is 来判断是否重新渲染。在更新后传入相同引用(例如 setPhrase(model))意味着不会重新渲染。
  • 结果:数据库更新(文本、笔记、语言等)不会显示,直到用户离开该页面再返回,此时新的订阅会得到一个新的 引用。
修复:存储包装对象,让每次发射都产生新引用

订阅单个模型(例如 findAndObserve)并将其存储在 React 状态中时,不要直接存储原始模型。存储一个包装对象,这样每次发射都会用新对象更新状态:

const [phraseState, setPhraseState] = useState<
  { phrase: Phrase; _key: number } | null
>(null);

useEffect(() => {
  if (!id) return;
  const sub = db.collections
    .get<Phrase>(PHRASE_TABLE)
    .findAndObserve(id)
    .subscribe((result) => {
      setPhraseState({ phrase: result, _key: result.updatedAt });
    });
  return () => sub.unsubscribe();
}, [id, db]);

const phrase = phraseState?.phrase ?? null;
  • 在组件中使用派生出的 phrase。持久化到数据库的更新会重新发射,用新的包装对象更新 phraseState,并触发 重新渲染。
查询 observe() 与数组
  • query.observe() 会发射模型数组。查询结果集发生变化时,WatermelonDB 通常会发射一个新的数组引用,因此 setState(results) 通常会触发重新渲染。
  • 如果在订阅回调中构建派生数据(例如 linked.filter(...)、assignments)并对其调用 setState,你传入的已经是新引用,无需额外包装。

withObservables(HOC)

  • withObservables(triggerProps, getObservables) 会将 Observable 值作为 props 注入,并且始终向 setState 传入一个新的状态对象(例如 { values, isFetching }),因此即使模型引用没有变化,React 也会在每次发射时重新渲染。
  • 当你可以**观察作为 prop 传入的模型(或查询)**时,请使用它:例如 withObservables(['attempt'], ({ attempt }) => ({ attempt: attempt.observe(), ... }))。 请参阅 features/lesson/components/AttemptCard.tsx 中的 AttemptCard。
  • 对于路由参数(例如 id),通常会在 useEffect 中手动订阅(例如 findAndObserve(id))。这种情况下,请使用上文的包装对象 模式,而不是存储原始模型。

本项目中的模型模式

  • 模型:database/models/(例如 Phrase、Lesson、Attempt、 Translation、Deck)。使用 @field、@writer 和静态辅助方法(例如 Phrase.findOrCreatePhrase、Lesson.addLesson)。
  • 观察:从 @nozbe/watermelondb/react 使用 useDatabase();然后调用 collection.findAndObserve(id) 或 query.observe().subscribe(...)。
  • Schema/表:database/schema.ts;通过 db.collections.get<Model>(TABLE) 访问集合。

快速参考

场景模式重新渲染保证
按 id 查找单个模型(例如详情页)findAndObserve + 包装器状态 { model, _key }是
查询结果(列表)query.observe() + setState(results) 或派生结构是(新数组/引用)
将模型作为属性传入withObservables(['model'], ({ model }) => ({ model: model.observe() }))是(HOC 使用新的状态结构)

资源

  • DeepWiki:Nozbe/WatermelonDB — findAndObserve、observe()、withObservables、模型更新。
  • 项目:PhraseDetailScreen、LessonDetailScreen、SetDetailScreen (findAndObserve + 包装器);AttemptCard(withObservables)。

评论

支持 Markdown