Skip to content

Примеры на React ​

Паттерны с worker-sync-db/react. Peer: React 18+.

Настройка Provider ​

tsx
import { useEffect, useState, type ReactNode } from 'react';
import {
  createDatabase,
  defineSchema,
  sharedWorkerUrl,
  dedicatedWorkerUrl,
  type DatabaseClient,
} from 'worker-sync-db';
import { DatabaseProvider } from 'worker-sync-db/react';

const schema = defineSchema({
  todos: {
    keyPath: 'id',
    indexes: { byStatus: 'status' },
  },
});

export function AppDatabase({ children }: { children: ReactNode }) {
  const [db, setDb] = useState<DatabaseClient<typeof schema> | null>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    let client: DatabaseClient<typeof schema> | undefined;
    let cancelled = false;

    void createDatabase({
      schema,
      dbName: 'react-todos',
      sharedWorker: sharedWorkerUrl,
      dedicatedWorker: dedicatedWorkerUrl,
    })
      .then((opened) => {
        if (cancelled) {
          opened.close();
          return;
        }
        client = opened;
        setDb(opened);
      })
      .catch((err: unknown) => {
        setError(err instanceof Error ? err.message : String(err));
      });

    return () => {
      cancelled = true;
      client?.close();
    };
  }, []);

  if (error) return <p role="alert">{error}</p>;
  if (!db) return <p>Opening local database…</p>;

  return <DatabaseProvider db={db}>{children}</DatabaseProvider>;
}

Живой список с useQuery ​

tsx
import { useState } from 'react';
import { useQuery, useDatabase } from 'worker-sync-db/react';

export function TodoList() {
  const db = useDatabase();
  const [title, setTitle] = useState('');
  const { data: todos = [], loading, error } = useQuery('todos');

  if (!db) return null;
  if (loading) return <p>Loading…</p>;
  if (error) return <p role="alert">{String(error)}</p>;

  return (
    <div>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          const value = title.trim();
          if (!value) return;
          void db.put('todos', {
            id: crypto.randomUUID(),
            title: value,
            status: 'open',
          });
          setTitle('');
        }}
      >
        <input
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          placeholder="New todo"
        />
        <button type="submit">Add</button>
      </form>

      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            <label>
              <input
                type="checkbox"
                checked={todo.status === 'done'}
                onChange={() =>
                  void db.put('todos', {
                    ...todo,
                    status: todo.status === 'done' ? 'open' : 'done',
                  })
                }
              />
              {todo.title}
            </label>
            <button type="button" onClick={() => void db.delete('todos', todo.id)}>
              Delete
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

Фильтр запроса + статус sync ​

tsx
import { useQuery, useSyncStatus } from 'worker-sync-db/react';

export function OpenTodos() {
  const sync = useSyncStatus();
  const { data: open = [] } = useQuery('todos', {
    index: 'byStatus',
    range: { lower: 'open', upper: 'open' },
  });

  return (
    <section>
      <p>
        Pending sync: {sync?.pending ?? 0}
        {sync?.lastError ? ` · ${sync.lastError}` : ''}
      </p>
      <ul>
        {open.map((t) => (
          <li key={t.id}>{t.title}</li>
        ))}
      </ul>
    </section>
  );
}

useSubscribe для side effects ​

tsx
import { useSubscribe } from 'worker-sync-db/react';

export function TodoToaster() {
  useSubscribe('todos', (event) => {
    if (event.type === 'put') {
      console.log('todo changed', event.id);
    }
  });
  return null;
}

Полное демо-приложение ​

Опубликованное демо на Pages — полноценный UI todos с mock remote sync:

Локально из корня репозитория:

bash
npm install
npm run build
npm run example:dev

MIT License · offline-first IndexedDB и синхронизация через SharedWorker