Skip to content

Add useRxDocument and useReplicationStatus React hooks#8272

Merged
pubkey merged 14 commits intomasterfrom
claude/rxdb-todo-app-XQKvS
Apr 13, 2026
Merged

Add useRxDocument and useReplicationStatus React hooks#8272
pubkey merged 14 commits intomasterfrom
claude/rxdb-todo-app-XQKvS

Conversation

@pubkey
Copy link
Copy Markdown
Owner

@pubkey pubkey commented Apr 2, 2026

The RxDB React plugin was missing hooks for subscribing to individual documents and monitoring replication state, requiring manual observable management in components.

Changes

  • useRxDocument(collection, primaryKey) — subscribes to collection.findOne(primaryKey).$, returns { result, loading, error } with live updates and cleanup on unmount
  • useReplicationStatus(replicationState) — subscribes to active$, error$, canceled$, received$, and sent$ on an RxReplicationState, returns { syncing, error, lastSyncedAt, canceled }
  • Docs — added "Single document subscription" and "Replication status" sections to docs-src/docs/react.md
  • Changelog — entries in orga/changelog/add-react-hooks.md
  • Tests — 9 new integration tests in test/react/react-hooks.test.tsx covering both hooks using real RxDB collections and replication state
const { result: hero, loading } = useRxDocument(heroCollection, heroId);

const { syncing, canceled, error, lastSyncedAt } = useReplicationStatus(replicationState);

Both hooks safely handle null/undefined inputs and clean up subscriptions on unmount.

claude added 6 commits April 2, 2026 10:58
- Dev-mode: warn when indexed fields are not in required array (Dexie compat)
- React: add useRxDocument hook for single-document subscriptions
- React: add useRxMutation hook for insert/update/remove with loading states
- React: add useReplicationStatus hook for replication observables
- Types: add InferDocType<T> as shorthand for ExtractDocumentTypeFromTypedRxJsonSchema

https://claude.ai/code/session_01AZM8huzJWCZWUXUo1vYEY1
The non-required index field check only applies to Dexie storage,
which already throws DXE1 at storage creation time. Removed the
generic warning from checkSchema() since other storages allow it.

https://claude.ai/code/session_01AZM8huzJWCZWUXUo1vYEY1
- Document useRxDocument, useRxMutation, useReplicationStatus in react.md
- Replace ExtractDocumentTypeFromTypedRxJsonSchema with InferDocType in:
  - TypeScript tutorial docs
  - vite-vanilla-ts example
  - Angular example
  - key-compression test
- Add changelog entries for new hooks and InferDocType

https://claude.ai/code/session_01AZM8huzJWCZWUXUo1vYEY1
… of InferDocType

InferDocType is the canonical type. ExtractDocumentTypeFromTypedRxJsonSchema
is kept as a deprecated alias pointing to InferDocType for backwards compat.

https://claude.ai/code/session_01AZM8huzJWCZWUXUo1vYEY1
@pubkey
Copy link
Copy Markdown
Owner Author

pubkey commented Apr 2, 2026

@copilot can we add tests for the new hooks?

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 2, 2026

@copilot can we add tests for the new hooks?

Added 13 tests in test/unit/react-hooks.test.ts (commit 5b921b4):

useRxDocument (7 tests):

  • Subscribes to collection.findOne(primaryKey).$ and returns the document
  • Returns null when the document does not exist
  • Emits updated document when the document changes
  • Emits null when the document is removed
  • Cleans up subscription on unsubscribe
  • Validates that non-RxCollection input fails the isRxCollection check
  • Handles null collection without throwing

useReplicationStatus (6 tests):

  • active$ emits syncing state changes
  • canceled$ emits false initially and true after cancel
  • received$ emits when documents are pulled
  • error$ emits on replication errors
  • All subscriptions are closed after unsubscribe()
  • Handles null replicationState without throwing

Also removed a leftover debug statement (window as any).collection = dbCollection from use-rx-query.ts.

Copilot AI changed the title Add React hooks for document subscription, mutations, and replication status Add React hooks for document subscription and replication status Apr 2, 2026
@pubkey
Copy link
Copy Markdown
Owner Author

pubkey commented Apr 2, 2026

@copilot remove these tests again, they are useless

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

⚠️ Verify Test Reproduction: Tests PASSED without the fix (unexpected)

The changed tests do not fail without the source changes from this PR. Please inspect whether the test changes actually test the bug that the source changes fix.

This workflow runs the changed tests without the source fix to verify they reproduce the bug.

Show output
...(truncated, showing last 200 of 1662 lines)
    ✔ should keep the attachments during migration (86ms)
    ✔ should delete the attachment during migration (86ms)
    ✔ should be able to change the attachment data during migration (86ms)
    ✔ should be able to call the defined function (72ms)
    ✔ should roundtrip compressible attachment through encryption and compression (161ms)
    ✔ should roundtrip non-compressible attachment through encryption and compression (156ms)
    ✔ insert with inline attachments computes digest and length (71ms)
    ✔ bulkInsert with inline attachments (71ms)
    ✔ upsert with inline attachments on new document (71ms)
    ✔ upsert with inline attachments on existing document preserves and merges (72ms)
    ✔ incrementalUpsert with inline attachments preserves existing (109ms)
    ✔ upsert with deleteExistingAttachments removes unlisted attachments (109ms)
    ✔ #455 attachments not working (112ms)
    ✔ calling allAttachments() fails when document has none (108ms)
    ✔ #4107 reproduce 412 error (152ms)
    ✔ getData() should return a Blob with the correct MIME type (107ms)
    ✔ getData() should preserve MIME type for binary types (107ms)
    ✔ getData() should preserve MIME type for application/json (107ms)
    ✔ should write multiple attachments in a single operation (107ms)
    ✔ should work with a single attachment (107ms)
    ✔ insert with _attachments: [] should succeed with no attachments (111ms)
    ✔ insert with _attachments: null should succeed with no attachments (111ms)
    ✔ should throw AT2 for invalid inline attachment (missing data) (159ms)
    ✔ should throw AT3 for duplicate attachment ids (159ms)

  attachments-compression.test.ts (mode: deflate)
    ✔ compress->decompress
    ✔ should insert one attachment
    ✔ should get the attachment
    ✔ should get the data
    ✔ should have a smaller size when compression is used
    ✔ should compress a compressible type (text/plain) and roundtrip correctly
    ✔ should NOT compress a non-compressible type (image/jpeg) but still roundtrip correctly
    ✔ should compress text but not jpeg on the same document
    ✔ should match wildcard patterns
    ✔ should match exact patterns
    ✔ should be case-insensitive
    ✔ should NOT match non-compressible types
    ✔ full roundtrip through storage should preserve MIME type on getData()
    ✔ full roundtrip should preserve MIME type for non-compressible type

  attachments-compression.test.ts (mode: gzip)
    ✔ compress->decompress
    ✔ should insert one attachment
    ✔ should get the attachment
    ✔ should get the data
    ✔ should have a smaller size when compression is used
    ✔ should compress a compressible type (text/plain) and roundtrip correctly
    ✔ should NOT compress a non-compressible type (image/jpeg) but still roundtrip correctly
    ✔ should compress text but not jpeg on the same document
    ✔ should match wildcard patterns
    ✔ should match exact patterns
    ✔ should be case-insensitive
    ✔ should NOT match non-compressible types
    ✔ full roundtrip through storage should preserve MIME type on getData()
    ✔ full roundtrip should preserve MIME type for non-compressible type

  migration-storage.test.ts (prev-major to newest (dexie))
    basic migrations
-------------- RxDB Open Core RxStorage -------------------------------
You are using the free Dexie.js based RxStorage implementation from RxDB https://rxdb.info/rx-storage-dexie.html?console=dexie 
While this is a great option, we want to let you know that there are faster storage solutions available in our premium plugins.
For professional users and production environments, we highly recommend considering these premium options to enhance performance and reliability.
 https://rxdb.info/premium/?console=dexie 
If you already purchased premium access you can disable this log by calling the setPremiumFlag() function from rxdb-premium/plugins/shared.
---------------------------------------------------------------------
      ✔ create both databases
      ✔ should migrate all documents (63ms)
      ✔ should migrate in parallel
      ✔ migrate new->new should also work
    issues
      ✔ migration with multiple collections (39ms)

  migration-storage.test.ts (newest to newest)
    basic migrations
      ✔ create both databases
      ✔ should migrate all documents
      ✔ should migrate in parallel
      ✔ migrate new->new should also work
    issues
      ✔ migration with multiple collections

  webmcp.test.ts
[WebMCPPolyfill] navigator.modelContext.clearContext() is deprecated and will be removed in the next major version. Unregister individual tools instead.
    ✔ should register query tool when registerWebMCP is called
    ✔ should wait for changes using wait_changes tool (52ms)
    ✔ should iterate over changes using checkpoint
    ✔ should execute modifier tools successfully (insert/upsert/delete)
    ✔ should unregister tools when collection is closed
    ✔ should not register modifier tools when readOnly is true
    ✔ should emit log$ and error$ events for executed tools
    ✔ should register tools for newly added collections dynamically

  crdt.test.ts
    ✔ should throw if the wrong conflict handler is set
    ✔ should automatically set the CRDT conflict handler
    ✔ should insert a document and initialize the crdt state (46ms)
    ✔ should insert document via bulkInsert (46ms)
    ✔ should insert the document (58ms)
    ✔ should insert the document with undefined argument (46ms)
    ✔ should respect the if-else logic (57ms)
    ✔ should delete the document via .remove (52ms)
    ✔ should update the document (51ms)
    ✔ should throw on incrementalModify (45ms)
    ✔ should throw on modify (45ms)
    ✔ should redirect patch through updateCRDT (51ms)
    ✔ should redirect incrementalRemove through updateCRDT (51ms)
    ✔ should redirect update through updateCRDT (51ms)
    ✔ init
    ✔ should merge 2 inserts correctly (58ms)
    ✔ should preserve schema default values during conflict resolution (91ms)
    ✔ should merge the +1 increments (133ms)

  population.test.js
    ✔ should allow to create a schema with a relation
    ✔ should allow primary as relation key
    ✔ should allow to create a schema with a relation in nested
    ✔ should allow to create relation of array
    ✔ should allow to create relation with nullable string
    ✔ throw if ref-type is no string
    ✔ throw if ref-type is no string (array)
    ✔ populate top-level-field
    ✔ populate nested field
    ✔ populate string-array
    ✔ populate with primary as ref
    ✔ populate top-level-field
    ✔ populate nested field
    ✔ #222 population not working when multiInstance: false

  leader-election.test.js
    .die()
      ✔ other instance applies on death of leader
    election
      ✔ a single instance should always elect itself as leader
      ✔ should not elect as leader if other instance is leader (152ms)
      ✔ when 2 instances apply at the same time, one should win (82ms)
      ✔ when many instances apply, one should win (129ms)
      ✔ when the leader dies, a new one should be elected (155ms)
    cleanup
      ✔ should properly call die() on the elector when the database is closed
    integration
      ✔ non-multiInstance should always be leader
      ✔ non-multiInstance: waitForLeadership should instant
      ✔ waitForLeadership: run once when instance becomes leader (57ms)

  backup.test.ts
    init
      ✔ add plugin
    .backupSingleDocument()
      ✔ should backup a single document
    RxDatabase.backup() live=false
      ✔ should backup all docs with attachments
      ✔ should emit write events
    RxDatabase.backup() live=true
      ✔ should backup ongoing writes (60ms)

  import-export.test.js
    ✔ export the collection (43ms)
    ✔ export encrypted as decrypted (43ms)
    ✔ import json (75ms)
    ✔ should not import if schema is different (77ms)
    ✔ should export a valid dump (42ms)
    ✔ export encrypted as decrypted (42ms)
    ✔ export with multiple collections (91ms)
    ✔ export 1 of 2 collections (42ms)
    ✔ import dump (77ms)
    ✔ should not import if schema is different (77ms)
    ✔ #319 collections must be created before importDump (91ms)
    ✔ #1396 import/export should work with attachments (46ms)

  database-lifecycle.ts
    ✔ do some writes updates and deletes and cleanups and reopens (46ms)

  plugin.test.js
    ✔ should not crash when a new plugin is added
    ✔ should crash when a plugin with the same name added already but it is NOT the same object
    ✔ should NOT crash when a plugin with the same name added already but it IS the same object
    ✔ full.node.ts should run without errors (639ms)
    ✔ createRxDatabase
    ✔ createRxCollection
    ✔ createRxSchema
    ✔ createRxDocument
    ✔ postCreateRxDocument
    ✔ postCleanup

  last.test.ts (memory)
    ✔ run a minimal performance test to ensure the performance function works (105ms)
    ✔ ensure all Memory RxStorage instances are closed
    ✔ ensure every db is cleaned up
    ✔ ensure all collections are closed
    ✔ ensure all BroadcastChannels are closed
    ✔ ensure all replication states are closed
    ✔ ensure all RemoteMessageChannels have been closed
    ✔ ensure all websockets have been closed
    ✔ ensure all leader electors are dead
    ✔ exit the process


  1309 passing (11s)


View full workflow run

Agent-Logs-Url: https://github.com/pubkey/rxdb/sessions/155fca14-88f5-4719-9a99-7073385e2a5b

Co-authored-by: pubkey <8926560+pubkey@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 2, 2026

@copilot remove these tests again, they are useless

Done. Removed the test file and its import in commit d941096.

@pubkey
Copy link
Copy Markdown
Owner Author

pubkey commented Apr 8, 2026

@copilot add docs. move changelogs to /changelog

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 8, 2026

@copilot add docs. move changelogs to /changelog

Docs were already in docs-src/docs/react.md from the earlier commit. Moved the changelog entries out of CHANGELOG.md and into orga/changelog/add-react-hooks.md in commit 0ece2b3.

Copilot AI changed the title Add React hooks for document subscription and replication status Add useRxDocument and useReplicationStatus React hooks Apr 8, 2026
@pubkey
Copy link
Copy Markdown
Owner Author

pubkey commented Apr 8, 2026

@copilot add tests. also ci fails with ./scripts/check-code-block-line-length.js

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 8, 2026

@copilot add tests. also ci fails with ./scripts/check-code-block-line-length.js

Fixed the CI failure and added tests in commit 3dfdd52:

  • CI fix: shortened the two long lines in the useReplicationStatus doc example (94 and 101 chars → split across multiple lines, all under 86 chars)
  • Tests: added 9 new tests to test/react/react-hooks.test.tsx — 6 for useRxDocument (initial loading state, null for missing doc, returns existing doc, live updates on change, null after remove, no-op on null collection) and 3 for useReplicationStatus (default state on null input, reflects syncing, canceled=true after cancel). All 19 react tests pass.

@pubkey
Copy link
Copy Markdown
Owner Author

pubkey commented Apr 10, 2026

@copilot instead of adding your own tests, use the existing tests for the react plugin

…act-ssr.test.ts

Agent-Logs-Url: https://github.com/pubkey/rxdb/sessions/8eed464c-9c8e-4da9-aaf4-cddcfd5bb8f0

Co-authored-by: pubkey <8926560+pubkey@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 10, 2026

@copilot instead of adding your own tests, use the existing tests for the react plugin

Done in commit 4faefea. Added useRxDocument and useReplicationStatus SSR tests to the existing test/react-ssr.test.ts, and reverted test/react/react-hooks.test.tsx back to its prior state. The new tests verify both hooks render correctly with renderToString and return the expected default state when useEffect doesn't run (SSR).

@pubkey pubkey merged commit 8f18f04 into master Apr 13, 2026
23 of 24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants