🐷

React + RxDB:3つのアプローチを徹底比較

に公開

RxDBをReactアプリで使う際、実は3つの選択肢があります。それぞれの特徴と使い分けを、コード例とともに解説します。

はじめに

RxDBは強力なオフラインファースト対応のデータベースですが、Reactとの統合方法には複数のアプローチが存在します。本記事では、以下の3つの方法を比較します:

  1. RxDB単体 - 手動でサブスクリプション管理
  2. rxdb-hooks - React専用の軽量フック
  3. TanStack DB - TanStackエコシステム統合

アプローチ1:RxDB単体(手動サブスクリプション)

最もシンプルですが、ボイラープレートが多くなります。

import { useEffect, useState } from 'react'

function TodoList({ rxDatabase }) {
  const [todos, setTodos] = useState([])
  const [loading, setLoading] = useState(true)

  // 手動でサブスクリプション管理が必要
  useEffect(() => {
    const subscription = rxDatabase.todos
      .find()
      .$ // RxDB Observable
      .subscribe(documents => {
        setTodos(documents)
        setLoading(false)
      })

    // クリーンアップも忘れずに
    return () => subscription.unsubscribe()
  }, [rxDatabase])

  const addTodo = async (text) => {
    await rxDatabase.todos.insert({
      id: crypto.randomUUID(),
      text,
      completed: false
    })
  }

  const toggleTodo = async (id) => {
    const doc = await rxDatabase.todos.findOne(id).exec()
    await doc.patch({ completed: !doc.completed })
  }

  if (loading) return 読み込み中...

  return (
    
      {todos.map(todo => (
        
          <input
            type="checkbox"
            checked={todo.completed}
            onChange={() => toggleTodo(todo.id)}
          />
          {todo.text}
        
      ))}
      <button onClick={() => addTodo('新しいTodo')}>追加
    
  )
}

メリット

  • 依存関係が少ない
  • RxDBの機能を直接使える
  • シンプルなアプリには十分

デメリット

  • useState + useEffect のボイラープレートが多い
  • サブスクリプション管理を手動で行う必要がある
  • コンポーネントごとに同じパターンを繰り返す

アプローチ2:rxdb-hooks(推奨!)

React + RxDB専用に設計された軽量ライブラリです。

import { useRxQuery, useRxDB } from 'rxdb-hooks'

function TodoList() {
  const db = useRxDB()
  
  // たった1行でリアクティブなクエリ!
  const { result: todos, isFetching } = useRxQuery(
    db.todos.find()
  )

  const addTodo = async (text) => {
    await db.todos.insert({
      id: crypto.randomUUID(),
      text,
      completed: false
    })
    // 自動的にUIが更新される
  }

  const toggleTodo = async (id) => {
    const doc = await db.todos.findOne(id).exec()
    await doc.patch({ completed: !doc.completed })
  }

  if (isFetching) return 読み込み中...

  return (
    
      {todos?.map(todo => (
        
          <input
            type="checkbox"
            checked={todo.completed}
            onChange={() => toggleTodo(todo.id)}
          />
          {todo.text}
        
      ))}
      <button onClick={() => addTodo('新しいTodo')}>追加
    
  )
}

セットアップ

import { RxDBProvider } from 'rxdb-hooks'

function App() {
  return (
    
      
    
  )
}

メリット

  • ✅ ボイラープレートがほぼゼロ
  • ✅ RxDBに特化した設計で使いやすい
  • ✅ 軽量(RxDB専用なので余計な機能がない)
  • ✅ RxDBのクエリをそのまま使える
  • ✅ 自動的にサブスクリプション管理
  • ✅ ローディング状態を自動で管理

デメリット

  • React専用(Vue、Svelteなどでは使えない)

アプローチ3:TanStack DB

TanStackエコシステムとの統合を提供します。

import { createCollection, useQuery } from '@tanstack/react-db'
import { rxdbCollectionOptions } from '@tanstack/rxdb-db-collection'

// セットアップ(コンポーネント外で1回だけ)
const todosCollection = createCollection(
  rxdbCollectionOptions({
    rxCollection: myDatabase.todos,
    startSync: true
  })
)

function TodoList() {
  // TanStack Queryのような使用感
  const { data: todos = [], isLoading } = useQuery({
    queryKey: ['todos'],
    queryFn: () => todosCollection.getAll()
  })

  const addTodo = async (text) => {
    await todosCollection.insert({
      id: crypto.randomUUID(),
      text,
      completed: false
    })
  }

  const toggleTodo = async (id) => {
    const todo = todosCollection.getById(id)
    await todosCollection.update(id, { 
      completed: !todo.completed 
    })
  }

  if (isLoading) return 読み込み中...

  return (
    
      {todos.map(todo => (
        
          <input
            type="checkbox"
            checked={todo.completed}
            onChange={() => toggleTodo(todo.id)}
          />
          {todo.text}
        
      ))}
      <button onClick={() => addTodo('新しいTodo')}>追加
    
  )
}

メリット

  • ✅ TanStack Query、Router等との統合がスムーズ
  • ✅ React、Vue、Solid、Svelteなど複数フレームワーク対応
  • ✅ TanStack Queryの高度なキャッシング戦略を活用
  • ✅ 統一されたAPIで学習コストが低い

デメリット

  • RxDBだけのために導入するには重い
  • rxdb-hooksに比べて抽象化レイヤーが増える
  • RxDBの機能を直接使うには追加の設定が必要

バックエンド同期について

重要なポイント:どのアプローチでもRxDBの同期機能は独立して動作します

import { replicateRxCollection } from 'rxdb/plugins/replication'

// RxDBで同期設定(どのアプローチでも同じ)
const replicationState = replicateRxCollection({
  collection: db.todos,
  pull: { 
    handler: async (checkpoint, batchSize) => {
      const response = await fetch('/api/todos/pull', {
        method: 'POST',
        body: JSON.stringify({ checkpoint, batchSize })
      })
      return response.json()
    }
  },
  push: { 
    handler: async (docs) => {
      await fetch('/api/todos/push', {
        method: 'POST',
        body: JSON.stringify({ docs })
      })
    }
  }
})

rxdb-hooks または TanStack DB を使う場合:

  • RxDBで同期が実行されると、変更は自動的にUIに反映される
  • 追加のコードは不要
  • リアクティブなフックが変更を検知して自動で再レンダリング

RxDB単体 の場合:

  • サブスクリプションを適切に設定していれば同じように動作
  • ただし、各コンポーネントで手動管理が必要

比較表

項目 RxDB単体 rxdb-hooks TanStack DB
ボイラープレート 多い 少ない 少ない
学習コスト 中(TanStack知識が必要)
依存関係 最小
フレームワーク対応 手動実装次第 React専用 複数対応
RxDB統合 直接 最適化済み 抽象化レイヤー経由
パフォーマンス
おすすめ度 ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐

どれを選ぶべきか?

rxdb-hooksを選ぶべき場合(大多数)

  • ✅ Reactアプリを開発している
  • ✅ RxDBを最大限活用したい
  • ✅ シンプルで軽量なソリューションが欲しい
  • ✅ RxDBのクエリAPIに慣れている

TanStack DBを選ぶべき場合

  • ✅ 既にTanStack Query、Routerなどを使っている
  • ✅ 複数フレームワーク対応のライブラリを作っている
  • ✅ TanStackエコシステムで統一したい
  • ✅ TanStack Queryのキャッシング戦略が必要

RxDB単体を使うべき場合

  • ✅ 非常にシンプルなアプリ
  • ✅ 依存関係を最小限にしたい
  • ✅ カスタマイズが必要

結論

ほとんどのReact + RxDBプロジェクトには、rxdb-hooksがベストチョイスです。

理由:

  1. RxDB専用に設計されているため、APIが自然
  2. 軽量で追加のオーバーヘッドがほぼない
  3. RxDBの全機能を直接使える
  4. ボイラープレートが最小限

TanStack DBは素晴らしいライブラリですが、既にrxdb-hooksで解決されている問題に対して、より重厚なソリューションを提供しているという印象です。TanStackエコシステムに深く依存している場合や、マルチフレームワーク対応が必要な場合にのみ、TanStack DBを検討すると良いでしょう。

インストール

# rxdb-hooksを使う場合(推奨)
npm install rxdb rxdb-hooks

# TanStack DBを使う場合
npm install rxdb @tanstack/react-db @tanstack/rxdb-db-collection

参考リンク


この記事が、RxDBとReactの統合方法選びの参考になれば幸いです!

Discussion