watermelondb
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWatermelonDB Model & Observation
WatermelonDB 模型与观测
Overview
概述
This skill covers WatermelonDB models (), observation
(reactive queries), and ensuring React re-renders when observed data
changes. Use it when working with , ,
, or any screen that subscribes to DB changes.
database/models/findAndObservequery.observe()withObservables本技能涵盖WatermelonDB模型()、观测(响应式查询)以及确保React在观测数据变更时重新渲染的内容。适用于使用、、,或任何订阅数据库变更的页面开发场景。
database/models/findAndObservequery.observe()withObservablesObservation & React Re-rendering
观测与React重新渲染
findAndObserve
and same-reference emission
findAndObservefindAndObserve
与相同引用发射
findAndObserve- (on a collection): Fetches a record by ID, returns an Observable that emits immediately on subscribe and whenever the record is updated or deleted.
findAndObserve(id) - When a model is updated (e.g. via or
model.update()methods), the observable emits the same object reference with updated properties — it does not emit a new model instance.@writer
- (针对集合):通过ID获取一条记录,返回一个Observable,在订阅时立即发射数据,并在记录更新或删除时再次发射。
findAndObserve(id) - 当模型更新时(例如通过或
model.update()方法),该Observable会发射相同的对象引用,但属性已更新——它不会发射新的模型实例。@writer
React useState
bailout
useStateReact useState
跳过更新机制
useState- uses
useStateto decide whether to re-render. Passing the same reference (e.g.Object.is) after an update means no re-render.setPhrase(model) - Result: DB updates (text, note, language, etc.) don’t appear until the user navigates away and back, when a new subscription yields a fresh reference.
- 使用
useState来决定是否重新渲染。如果更新后传递相同的引用(例如Object.is),则不会触发重新渲染。setPhrase(model) - 结果:数据库的更新(文本、备注、语言等)不会立即显示,直到用户导航离开并返回页面,此时新的订阅会生成一个新的引用。
Fix: store a wrapper so each emit is a new reference
修复方案:存储包装对象,确保每次发射都是新引用
When subscribing to a single model (e.g. ) and storing it in
React state, don’t store the raw model. Store a wrapper so every emission
updates state with a new object:
findAndObservets
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;- Use (derived) everywhere in the component. Updates persisted to the DB will re-emit, update
phrasewith a new wrapper, and trigger a re-render.phraseState
当订阅单个模型(例如)并将其存储到React状态时,不要存储原始模型。应存储一个包装对象,这样每次发射都会用新对象更新状态:
findAndObservets
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
Query observe()
and arrays
observe()查询 observe()
与数组
observe()- emits arrays of models. When the query result set changes, WatermelonDB typically emits a new array reference, so
query.observe()usually triggers re-renders.setState(results) - If you build derived data (e.g. ,
linked.filter(...)) in the subscribe callback andassignmentsthat, you’re already passing new references — no extra wrapper needed.setState
- 发射模型的数组。当查询结果集变更时,WatermelonDB通常会发射新的数组引用,因此
query.observe()通常会触发重新渲染。setState(results) - 如果您在订阅回调中构建派生数据(例如、
linked.filter(...))并将其存入状态,您已经在传递新的引用——无需额外的包装对象。assignments
withObservables
(HOC)
withObservableswithObservables
(高阶组件)
withObservables- injects observable values as props and always passes a new state object into
withObservables(triggerProps, getObservables)(e.g.setState), so React re-renders on each emission even when model references are unchanged.{ values, isFetching } - Use it when you can observe a model (or query) passed as a prop: e.g.
. See
withObservables(['attempt'], ({ attempt }) => ({ attempt: attempt.observe(), ... }))inAttemptCard.features/lesson/components/AttemptCard.tsx - For route params (e.g. ) you typically subscribe manually in
id(e.g.useEffect). In that case, use the wrapper pattern above instead of storing the raw model.findAndObserve(id)
- 将可观测值作为props注入组件,并且始终会将新的状态对象(例如
withObservables(triggerProps, getObservables))传递给{ values, isFetching },因此即使模型引用未改变,React也会在每次发射时重新渲染。setState - 适用于模型作为props传入的场景:例如。可参考
withObservables(['attempt'], ({ attempt }) => ({ attempt: attempt.observe(), ... }))中的features/lesson/components/AttemptCard.tsx组件。AttemptCard - 对于路由参数(例如),通常需要在
id中手动订阅(例如useEffect)。这种情况下,请使用上述的包装对象模式,而非存储原始模型。findAndObserve(id)
Model patterns in this project
项目中的模型模式
- Models: (e.g.
database/models/,Phrase,Lesson,Attempt,Translation). UseDeck,@field, and static helpers (e.g.@writer,Phrase.findOrCreatePhrase).Lesson.addLesson - Observation: from
useDatabase(); then@nozbe/watermelondb/reactorcollection.findAndObserve(id).query.observe().subscribe(...) - Schema/tables: ; collection access via
database/schema.ts.db.collections.get<Model>(TABLE)
- 模型:位于目录(例如
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)
Quick reference
速查参考
| Scenario | Pattern | Re-render guarantee |
|---|---|---|
Single model by | | Yes |
| Query results (list) | | Yes (new array/refs) |
| Model passed as prop | | Yes (HOC uses new state shape) |
| 场景 | 实现模式 | 重新渲染保障 |
|---|---|---|
通过 | | 是 |
| 查询结果(列表页) | | 是(新数组/引用) |
| 模型作为props传入 | | 是(高阶组件使用新状态结构) |
Resources
参考资源
- DeepWiki: Nozbe/WatermelonDB —
,
findAndObserve,observe(), model updates.withObservables - Project: ,
PhraseDetailScreen,LessonDetailScreen(findAndObserve + wrapper);SetDetailScreen(withObservables).AttemptCard
- 官方文档:Nozbe/WatermelonDB — 包含、
findAndObserve、observe()、模型更新等内容。withObservables - 项目示例:、
PhraseDetailScreen、LessonDetailScreen(使用findAndObserve + 包装对象);SetDetailScreen(使用withObservables)。AttemptCard