-
Notifications
You must be signed in to change notification settings - Fork 200
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added support for react 18's suspense feature in DataSync
- Loading branch information
1 parent
3d727b1
commit 0d289cb
Showing
3 changed files
with
154 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import React, { useState, useEffect, useSyncExternalStore } from 'react'; | ||
import { DataSubscription } from './ihp-datasync.js'; | ||
|
||
/** | ||
* Returns the result of the current query in real-time. Suspends while the data is still being fetched from the server. | ||
* @example | ||
* const messages = useQuery(query('messages').orderBy('createdAt')); | ||
*/ | ||
export function useQuery(queryBuilder) { | ||
const dataSubscription = DataSubscriptionStore.get(queryBuilder.query); | ||
|
||
if (dataSubscription.isConnected) { | ||
const records = useSyncExternalStore(dataSubscription.subscribe, dataSubscription.getRecords) | ||
return records; | ||
} else if (dataSubscription.connectError) { | ||
throw dataSubscription.connectError; | ||
} else { | ||
throw dataSubscription.createOnServerPromise; | ||
} | ||
} | ||
|
||
export class DataSubscriptionStore { | ||
static queryMap = new Map(); | ||
static get(query) { | ||
const strinigifiedQuery = JSON.stringify(query); | ||
const existingSubscription = DataSubscriptionStore.queryMap.get(strinigifiedQuery) | ||
|
||
if (existingSubscription) { | ||
return existingSubscription; | ||
} else { | ||
const subscription = new DataSubscription(query); | ||
subscription.createOnServer(); | ||
subscription.onClose = () => { DataSubscriptionStore.queryMap.delete(strinigifiedQuery); }; | ||
|
||
DataSubscriptionStore.queryMap.set(strinigifiedQuery, subscription); | ||
|
||
// If the query changes very rapid in `useQuery` it can happen that the `dataSubscription.subscribe` | ||
// is never called at all. In this case we have a unused DataSubscription laying around. We avoid | ||
// to many open connections laying around by trying to close them a second after opening them. | ||
// A second is enough time for react to call the subscribe function. If it's not called by then, | ||
// we most likely deal with a dead subscription, so we close it. | ||
setTimeout(() => { | ||
subscription.closeIfNotUsed(); | ||
}, 1000); | ||
|
||
return subscription; | ||
} | ||
} | ||
} |