diff --git a/.changeset/typed-providers-query.md b/.changeset/typed-providers-query.md new file mode 100644 index 000000000..510201147 --- /dev/null +++ b/.changeset/typed-providers-query.md @@ -0,0 +1,5 @@ +--- +'@electric-sql/pglite-react': patch +--- + +Make live query hooks use databases supplied by typed PGlite providers. diff --git a/packages/pglite-react/src/provider.tsx b/packages/pglite-react/src/provider.tsx index f1b5f70e2..cfb16cc27 100644 --- a/packages/pglite-react/src/provider.tsx +++ b/packages/pglite-react/src/provider.tsx @@ -16,11 +16,12 @@ interface PGliteProviderSet { usePGlite: UsePGlite } -/** - * Create a typed set of {@link PGliteProvider} and {@link usePGlite}. - */ -function makePGliteProvider(): PGliteProviderSet { - const ctx = createContext(undefined) +const defaultContext = createContext(undefined) + +function makePGliteProviderSet( + ctx: React.Context, + bridgeDefaultContext: boolean, +): PGliteProviderSet { return { usePGlite: ((db?: T) => { const dbProvided = useContext(ctx) @@ -36,11 +37,28 @@ function makePGliteProvider(): PGliteProviderSet { return dbProvided }) as UsePGlite, PGliteProvider: ({ children, db }: Props) => { - return {children} + const provider = {children} + + return bridgeDefaultContext ? ( + {provider} + ) : ( + provider + ) }, } } -const { PGliteProvider, usePGlite } = makePGliteProvider() +/** + * Create a typed set of {@link PGliteProvider} and {@link usePGlite}. + */ +function makePGliteProvider(): PGliteProviderSet { + const ctx = createContext(undefined) + return makePGliteProviderSet(ctx, true) +} + +const { PGliteProvider, usePGlite } = makePGliteProviderSet( + defaultContext, + false, +) export { makePGliteProvider, PGliteProvider, usePGlite } diff --git a/packages/pglite-react/test/provider.test.tsx b/packages/pglite-react/test/provider.test.tsx index 651052e60..8d9b01a52 100644 --- a/packages/pglite-react/test/provider.test.tsx +++ b/packages/pglite-react/test/provider.test.tsx @@ -1,10 +1,15 @@ -import { describe, it, expect } from 'vitest' +import { describe, it, expect, vi } from 'vitest' import { renderHook } from '@testing-library/react' import { waitFor } from '@testing-library/dom' import React from 'react' import { PGlite } from '@electric-sql/pglite' import { live, PGliteWithLive } from '@electric-sql/pglite/live' -import { makePGliteProvider, PGliteProvider, usePGlite } from '../src' +import { + makePGliteProvider, + PGliteProvider, + useLiveQuery, + usePGlite, +} from '../src' describe('provider', () => { it('can receive PGlite', async () => { @@ -40,4 +45,43 @@ describe('provider', () => { await waitFor(() => expect(result.current).toBe(db)) }) + + it('makes useLiveQuery available under a typed provider', async () => { + const initialResults = { + rows: [{ value: 1 }], + fields: [{ name: 'value', dataTypeID: 23 }], + } + const query = vi.fn( + ( + _query: string, + _params: unknown[] | undefined | null, + callback: (results: typeof initialResults) => void, + ) => { + callback(initialResults) + return Promise.resolve({ + initialResults, + subscribe: vi.fn(), + unsubscribe: vi.fn(async () => undefined), + refresh: vi.fn(async () => undefined), + }) + }, + ) + const db = { live: { query } } as unknown as PGliteWithLive + const { PGliteProvider: PGliteProviderTyped } = + makePGliteProvider() + const wrapper = ({ children }: { children: React.ReactNode }) => { + return {children} + } + + const { result } = renderHook(() => useLiveQuery('SELECT 1 AS value'), { + wrapper, + }) + + await waitFor(() => expect(result.current?.rows).toEqual([{ value: 1 }])) + expect(query).toHaveBeenCalledWith( + 'SELECT 1 AS value', + undefined, + expect.any(Function), + ) + }) })