Skip to content

Commit 2dfe778

Browse files
add settings_will_update hook in order to override isFetching
1 parent 1c163a6 commit 2dfe778

File tree

3 files changed

+26
-18
lines changed

3 files changed

+26
-18
lines changed

packages/common/src/client/watched/WatchedQuery.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,15 @@ export enum WatchedQueryListenerEvent {
7171
ON_DATA = 'onData',
7272
ON_ERROR = 'onError',
7373
ON_STATE_CHANGE = 'onStateChange',
74+
SETTINGS_WILL_UPDATE = 'settingsWillUpdate',
7475
CLOSED = 'closed'
7576
}
7677

7778
export interface WatchedQueryListener<Data> extends BaseListener {
7879
[WatchedQueryListenerEvent.ON_DATA]?: (data: Data) => void | Promise<void>;
7980
[WatchedQueryListenerEvent.ON_ERROR]?: (error: Error) => void | Promise<void>;
8081
[WatchedQueryListenerEvent.ON_STATE_CHANGE]?: (state: WatchedQueryState<Data>) => void | Promise<void>;
82+
[WatchedQueryListenerEvent.SETTINGS_WILL_UPDATE]?: () => void;
8183
[WatchedQueryListenerEvent.CLOSED]?: () => void | Promise<void>;
8284
}
8385

packages/common/src/client/watched/processors/AbstractQueryProcessor.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { AbstractPowerSyncDatabase } from '../../../client/AbstractPowerSyncDatabase.js';
22
import { MetaBaseObserver } from '../../../utils/MetaBaseObserver.js';
3-
import { WatchedQuery, WatchedQueryListener, WatchedQueryOptions, WatchedQueryState } from '../WatchedQuery.js';
3+
import {
4+
WatchedQuery,
5+
WatchedQueryListener,
6+
WatchedQueryListenerEvent,
7+
WatchedQueryOptions,
8+
WatchedQueryState
9+
} from '../WatchedQuery.js';
410

511
/**
612
* @internal
@@ -87,6 +93,8 @@ export abstract class AbstractQueryProcessor<
8793

8894
this.options.watchOptions = settings;
8995

96+
this.iterateListeners((l) => l[WatchedQueryListenerEvent.SETTINGS_WILL_UPDATE]?.());
97+
9098
if (!this.state.isFetching && this.reportFetching) {
9199
await this.updateState({
92100
isFetching: true

packages/react/src/hooks/watched/useWatchedQuery.ts

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,14 @@ export const useWatchedQuery = <RowType = unknown>(
3535
}
3636

3737
const [watchedQuery, setWatchedQuery] = React.useState(createWatchedQuery);
38-
const updatingPromise = React.useRef<Promise<void> | null>();
38+
const disposePendingUpdateListener = React.useRef<() => void | null>(null);
3939

4040
React.useEffect(() => {
4141
watchedQuery?.close();
4242
setWatchedQuery(createWatchedQuery);
4343

4444
return () => {
45+
disposePendingUpdateListener.current?.();
4546
watchedQuery?.close();
4647
};
4748
}, [powerSync, active]);
@@ -55,33 +56,30 @@ export const useWatchedQuery = <RowType = unknown>(
5556
* as soon as the query has been updated. This prevents a result flow where e.g. the hook:
5657
* - already returned a result: isLoading, isFetching are both false
5758
* - the query is updated, but the state is still isFetching=false from the previous state
58-
* We override the isFetching status while the `updateSettings` method is running (if we report `isFetching`).
59-
* The query could change multiple times while settings are being updated. In order to cater for this, we
60-
* track the latest update operation promise.
59+
* We override the isFetching status while the `updateSettings` method is running (if we report `isFetching`),
60+
* we override this value just until the `updateSettings` method itself will update the `isFetching` status.
61+
* We achieve this by registering a `settingsWillUpdate` listener on the `WatchedQuery`. This will fire
62+
* just before the `isFetching` status is updated.
6163
*/
6264
if (queryChanged) {
6365
// Keep track of this pending operation
64-
let pendingUpdate = watchedQuery?.updateSettings({
66+
watchedQuery?.updateSettings({
6567
query,
6668
throttleMs: hookOptions.throttleMs,
6769
reportFetching: hookOptions.reportFetching
6870
});
69-
70-
// Keep track of the latest pending update operation
71-
updatingPromise.current = pendingUpdate;
72-
73-
// Clear the latest pending update operation when the latest
74-
// operation has completed.
75-
pendingUpdate?.then(() => {
76-
// Only clear if this iteration was the latest iteration
77-
if (pendingUpdate == updatingPromise.current) {
78-
updatingPromise.current = null;
71+
// This could have been called multiple times, clear any old listeners.
72+
disposePendingUpdateListener.current?.();
73+
disposePendingUpdateListener.current = watchedQuery?.registerListener({
74+
settingsWillUpdate: () => {
75+
// We'll use the fact that we have a listener at all as an indication
76+
disposePendingUpdateListener.current?.();
77+
disposePendingUpdateListener.current = null;
7978
}
8079
});
8180
}
8281

83-
const shouldReportCurrentlyFetching = (hookOptions.reportFetching ?? true) && !!updatingPromise.current;
84-
82+
const shouldReportCurrentlyFetching = (hookOptions.reportFetching ?? true) && !!disposePendingUpdateListener.current;
8583
const result = useNullableWatchedQuerySubscription(watchedQuery);
8684
return {
8785
...result,

0 commit comments

Comments
 (0)