diff --git a/package-lock.json b/package-lock.json index 5f12c77..fea9a45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "replicache-react", - "version": "3.1.0", + "version": "4.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "replicache-react", - "version": "3.1.0", + "version": "4.0.0", "license": "ISC", "devDependencies": { "@esm-bundle/chai": "^4.3.4-fix.0", diff --git a/package.json b/package.json index 767ecd5..f01119c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "replicache-react", - "version": "3.1.0", + "version": "4.0.0", "description": "Miscellaneous utilities for using Replicache with React", "homepage": "https://replicache.dev", "repository": "github:rocicorp/replicache-react", diff --git a/src/index.test.tsx b/src/index.test.tsx index b4e843c..a091495 100644 --- a/src/index.test.tsx +++ b/src/index.test.tsx @@ -76,7 +76,7 @@ test('Batching of subscriptions', async () => { const dataA = useSubscribe( rep, // TODO: Use type param to get when new Replicache is released. - async tx => ((await tx.get('a')) as string | undefined) ?? null, + async tx => (await tx.get('a')) as string | undefined, null, ); renderLog.push('render A', dataA); @@ -86,7 +86,7 @@ test('Batching of subscriptions', async () => { function B({rep, dataA}: {rep: MyRep; dataA: string | null}) { const dataB = useSubscribe( rep, - async tx => ((await tx.get('b')) as string | undefined) ?? null, + async tx => (await tx.get('b')) as string | undefined, null, ); renderLog.push('render B', dataA, dataB); @@ -129,7 +129,7 @@ test('returning undefined', async () => { }, def, ); - return
{subResult === undefined ? 'undefined' : 'defined'}
; + return
{subResult}
; } const div = document.createElement('div'); @@ -141,10 +141,10 @@ test('returning undefined', async () => { }); render(, div); - expect(div.textContent).to.equal('defined'); + expect(div.textContent).to.equal('default'); await promise; await sleep(1); - expect(div.textContent).to.equal('undefined'); + expect(div.textContent).to.equal('default'); await rep.close(); }); diff --git a/src/index.ts b/src/index.ts index 5452efb..7d14138 100644 --- a/src/index.ts +++ b/src/index.ts @@ -25,13 +25,15 @@ function doCallback() { }); } -export function useSubscribe( - r: Subscribable | null | undefined, - query: (tx: Tx) => Promise, - def: R, +export type RemoveUndefined = T extends undefined ? never : T; + +export function useSubscribe( + r: Subscribable | null | undefined, + query: (tx: Tx) => Promise, + def: Default, deps: Array = [], -): R { - const [snapshot, setSnapshot] = useState(def); +) { + const [snapshot, setSnapshot] = useState(undefined); useEffect(() => { if (!r) { return; @@ -41,7 +43,7 @@ export function useSubscribe( onData: data => { // This is safe because we know that subscribe in fact can only return // `R` (the return type of query or def). - callbacks.push(() => setSnapshot(data as R)); + callbacks.push(() => setSnapshot(data as QueryRet)); if (!hasPendingCallback) { void Promise.resolve().then(doCallback); hasPendingCallback = true; @@ -51,8 +53,15 @@ export function useSubscribe( return () => { unsubscribe(); - setSnapshot(def); + setSnapshot(undefined); }; - }, [r, ...deps]); - return snapshot; + }, [r, def, ...deps]); + if (snapshot === undefined) { + return def; + } + // This RemoveUndefined is just here to make the return type easier to read. + // It should be exactly equivalent to what the type would be without this. + // For some reason declaring the return type to be + // RemoveUndefined | Default doesn't typecheck. + return snapshot as RemoveUndefined; }