Skip to content

Commit 9ca66f2

Browse files
use comparator as the option name for hooks. Update identify to keyBy for differential watches.
1 parent 053a286 commit 9ca66f2

File tree

19 files changed

+88
-76
lines changed

19 files changed

+88
-76
lines changed

.changeset/nine-pens-ring.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44

55
[Potentially breaking change] The `useQuery` hook results are now explicitly defined as readonly. These values should not be mutated.
66

7-
- Added the ability to limit re-renders by specifying a `differentiator` for query results. The `useQuery` hook will only emit `data` changes when the data has changed.
7+
- Added the ability to limit re-renders by specifying a `comparator` for query results. The `useQuery` hook will only emit `data` changes when the data has changed.
88

99
```javascript
1010
// The data here will maintain previous object references for unchanged items.
1111
const { data } = useQuery('SELECT * FROM lists WHERE name = ?', ['aname'], {
12-
differentiator: {
13-
identify: (item) => item.id,
12+
comparator: {
13+
keyBy: (item) => item.id,
1414
compareBy: (item) => JSON.stringify(item)
1515
}
1616
});

.changeset/swift-guests-explain.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
'@powersync/react': minor
33
---
44

5-
- Added the ability to limit re-renders by specifying a `differentiator` for query results. The `useQuery` hook will only emit `data` changes when the data has changed.
5+
- Added the ability to limit re-renders by specifying a `comparator` for query results. The `useQuery` hook will only emit `data` changes when the data has changed.
66

77
```javascript
88
// The data here will maintain previous object references for unchanged items.
99
const { data } = useQuery('SELECT * FROM lists WHERE name = ?', ['aname'], {
10-
differentiator: {
11-
identify: (item) => item.id,
10+
comparator: {
11+
keyBy: (item) => item.id,
1212
compareBy: (item) => JSON.stringify(item)
1313
}
1414
});

demos/react-supabase-todolist/src/app/views/sql-console/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ export default function SQLConsolePage() {
6363
* The query here will only emit results when the query data set changes.
6464
* Result sets are compared by serializing each item to JSON and comparing the strings.
6565
*/
66-
differentiator: {
67-
identify: (item: any) => JSON.stringify(item),
66+
comparator: {
67+
keyBy: (item: any) => JSON.stringify(item),
6868
compareBy: (item: any) => JSON.stringify(item)
6969
}
7070
});

packages/common/src/client/CustomQuery.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export class CustomQuery<RowType> implements Query<RowType> {
4444
differentialWatch(differentialWatchOptions: DifferentialWatchedQueryOptions<RowType>) {
4545
return new DifferentialQueryProcessor<RowType>({
4646
db: this.options.db,
47-
differentiator: differentialWatchOptions?.differentiator,
47+
comparator: differentialWatchOptions?.comparator,
4848
placeholderData: differentialWatchOptions?.placeholderData ?? [],
4949
watchOptions: {
5050
...this.resolveOptions(differentialWatchOptions),

packages/common/src/client/Query.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
DifferentialWatchedQuery,
44
DifferentialWatchedQueryOptions
55
} from './watched/processors/DifferentialQueryProcessor.js';
6-
import { ComparisonWatchedQuery } from './watched/processors/OnChangeQueryProcessor.js';
6+
import { StandardWatchedQuery } from './watched/processors/OnChangeQueryProcessor.js';
77
import { WatchedQueryOptions } from './watched/WatchedQuery.js';
88

99
/**
@@ -73,7 +73,7 @@ export interface Query<RowType> {
7373
* but note that the result set will not maintain internal object references to the previous result set. If internal object references are needed,
7474
* consider using {@link Query#differentialWatch} instead.
7575
*/
76-
watch(options?: StandardWatchedQueryOptions<RowType>): ComparisonWatchedQuery<ReadonlyArray<Readonly<RowType>>>;
76+
watch(options?: StandardWatchedQueryOptions<RowType>): StandardWatchedQuery<ReadonlyArray<Readonly<RowType>>>;
7777

7878
/**
7979
* Creates a {@link WatchedQuery} which watches and emits results of the linked query.

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

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ export interface WatchedQueryDifferential<RowType> {
4242
}
4343

4444
/**
45-
* Differentiator for incremental watched queries which allows to identify and compare items in the result set.
45+
* Comparator for differentially watched queries which keys and compares items in the result set.
4646
*/
47-
export interface WatchedQueryDifferentiator<RowType> {
47+
export interface DifferentialWatchedQueryComparator<RowType> {
4848
/**
4949
* Unique identifier for the item.
5050
*/
51-
identify: (item: RowType) => string;
51+
keyBy: (item: RowType) => string;
5252
/**
5353
* Generates a key for comparing items with matching identifiers.
5454
*/
@@ -65,12 +65,12 @@ export interface DifferentialWatchedQueryOptions<RowType> extends WatchedQueryOp
6565
placeholderData?: RowType[];
6666

6767
/**
68-
* Differentiator used to identify and compare items in the result set.
69-
* If not provided, the default differentiator will be used which identifies items by their `id` property if available,
70-
* otherwise it uses JSON stringification of the entire item for identification and comparison.
71-
* @defaultValue {@link DEFAULT_WATCHED_QUERY_DIFFERENTIATOR}
68+
* Comparator used to identify and compare items in the result set.
69+
* If not provided, the default comparator will be used which keys items by their `id` property if available,
70+
* otherwise it uses JSON stringification of the entire item for keying and comparison.
71+
* @defaultValue {@link DEFAULT_DIFFERENTIAL_WATCHED_QUERY_COMPARATOR}
7272
*/
73-
differentiator?: WatchedQueryDifferentiator<RowType>;
73+
comparator?: DifferentialWatchedQueryComparator<RowType>;
7474
}
7575

7676
/**
@@ -99,7 +99,7 @@ export type DifferentialWatchedQuery<RowType> = WatchedQuery<
9999
*/
100100
export interface DifferentialQueryProcessorOptions<RowType>
101101
extends AbstractQueryProcessorOptions<RowType[], DifferentialWatchedQuerySettings<RowType>> {
102-
differentiator?: WatchedQueryDifferentiator<RowType>;
102+
comparator?: DifferentialWatchedQueryComparator<RowType>;
103103
}
104104

105105
type DataHashMap<RowType> = Map<string, { hash: string; item: RowType }>;
@@ -117,12 +117,12 @@ export const EMPTY_DIFFERENTIAL = {
117117
};
118118

119119
/**
120-
* Default implementation of the {@link Differentiator} for watched queries.
121-
* It identifies items by their `id` property if available, otherwise it uses JSON stringification
122-
* of the entire item for identification and comparison.
120+
* Default implementation of the {@link DifferentialWatchedQueryComparator} for watched queries.
121+
* It keys items by their `id` property if available, alternatively it uses JSON stringification
122+
* of the entire item for the key and comparison.
123123
*/
124-
export const DEFAULT_WATCHED_QUERY_DIFFERENTIATOR: WatchedQueryDifferentiator<any> = {
125-
identify: (item) => {
124+
export const DEFAULT_DIFFERENTIAL_WATCHED_QUERY_COMPARATOR: DifferentialWatchedQueryComparator<any> = {
125+
keyBy: (item) => {
126126
if (item && typeof item == 'object' && typeof item['id'] == 'string') {
127127
return item['id'];
128128
}
@@ -140,11 +140,11 @@ export class DifferentialQueryProcessor<RowType>
140140
extends AbstractQueryProcessor<ReadonlyArray<Readonly<RowType>>, DifferentialWatchedQuerySettings<RowType>>
141141
implements DifferentialWatchedQuery<RowType>
142142
{
143-
protected differentiator: WatchedQueryDifferentiator<RowType>;
143+
protected comparator: DifferentialWatchedQueryComparator<RowType>;
144144

145145
constructor(protected options: DifferentialQueryProcessorOptions<RowType>) {
146146
super(options);
147-
this.differentiator = options.differentiator ?? DEFAULT_WATCHED_QUERY_DIFFERENTIATOR;
147+
this.comparator = options.comparator ?? DEFAULT_DIFFERENTIAL_WATCHED_QUERY_COMPARATOR;
148148
}
149149

150150
/*
@@ -154,7 +154,7 @@ export class DifferentialQueryProcessor<RowType>
154154
current: RowType[],
155155
previousMap: DataHashMap<RowType>
156156
): { diff: WatchedQueryDifferential<RowType>; map: DataHashMap<RowType>; hasChanged: boolean } {
157-
const { identify, compareBy } = this.differentiator;
157+
const { keyBy, compareBy } = this.comparator;
158158

159159
let hasChanged = false;
160160
const currentMap = new Map<string, { hash: string; item: RowType }>();
@@ -175,7 +175,7 @@ export class DifferentialQueryProcessor<RowType>
175175
* We can replace items in the current array with previous object references if they are equal.
176176
*/
177177
for (const item of current) {
178-
const key = identify(item);
178+
const key = keyBy(item);
179179
const hash = compareBy(item);
180180
currentMap.set(key, { hash, item });
181181

@@ -225,8 +225,8 @@ export class DifferentialQueryProcessor<RowType>
225225

226226
// populate the currentMap from the placeholder data
227227
this.state.data.forEach((item) => {
228-
currentMap.set(this.differentiator.identify(item), {
229-
hash: this.differentiator.compareBy(item),
228+
currentMap.set(this.comparator.keyBy(item), {
229+
hash: this.comparator.compareBy(item),
230230
item
231231
});
232232
});

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,23 @@ import {
77
} from './AbstractQueryProcessor.js';
88
import { WatchedQueryComparator } from './comparators.js';
99

10-
export interface ComparisonWatchedQuerySettings<DataType> extends WatchedQueryOptions {
10+
/**
11+
* Settings for {@link WatchedQuery} instances created via {@link Query#watch}.
12+
*/
13+
export interface WatchedQuerySettings<DataType> extends WatchedQueryOptions {
1114
query: WatchCompatibleQuery<DataType>;
1215
}
1316

14-
export type ComparisonWatchedQuery<DataType> = WatchedQuery<DataType, ComparisonWatchedQuerySettings<DataType>>;
17+
/**
18+
* {@link WatchedQuery} returned from {@link Query#watch}.
19+
*/
20+
export type StandardWatchedQuery<DataType> = WatchedQuery<DataType, WatchedQuerySettings<DataType>>;
1521

1622
/**
1723
* @internal
1824
*/
1925
export interface OnChangeQueryProcessorOptions<Data>
20-
extends AbstractQueryProcessorOptions<Data, ComparisonWatchedQuerySettings<Data>> {
26+
extends AbstractQueryProcessorOptions<Data, WatchedQuerySettings<Data>> {
2127
comparator?: WatchedQueryComparator<Data>;
2228
}
2329

@@ -26,7 +32,7 @@ export interface OnChangeQueryProcessorOptions<Data>
2632
* Results are emitted on every change of the relevant tables.
2733
* @internal
2834
*/
29-
export class OnChangeQueryProcessor<Data> extends AbstractQueryProcessor<Data, ComparisonWatchedQuerySettings<Data>> {
35+
export class OnChangeQueryProcessor<Data> extends AbstractQueryProcessor<Data, WatchedQuerySettings<Data>> {
3036
constructor(protected options: OnChangeQueryProcessorOptions<Data>) {
3137
super(options);
3238
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/**
2+
* A basic comparator for incrementally watched queries. This performs a single comparison which
3+
* determines if the result set has changed. The {@link WatchedQuery} will only emit the new result
4+
* if a change has been detected.
5+
*/
16
export interface WatchedQueryComparator<Data> {
27
checkEquality: (current: Data, previous: Data) => boolean;
38
}
@@ -13,7 +18,8 @@ export type ArrayComparatorOptions<ItemType> = {
1318
};
1419

1520
/**
16-
* Compares array results of watched queries for incrementally watched queries created in the standard mode.
21+
* An efficient comparator for {@link WatchedQuery} created with {@link Query#watch}. This has the ability to determine if a query
22+
* result has changes without necessarily processing all items in the result.
1723
*/
1824
export class ArrayComparator<ItemType> implements WatchedQueryComparator<ItemType[]> {
1925
constructor(protected options: ArrayComparatorOptions<ItemType>) {}

packages/react/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,8 @@ function MyWidget() {
346346
// Note that isFetching is set (by default) whenever the query is being fetched/checked.
347347
// This will result in `MyWidget` re-rendering for any change to the `cats` table.
348348
const { data, isLoading, isFetching } = useQuery(`SELECT * FROM cats WHERE breed = 'tabby'`, [], {
349-
differentiator: {
350-
identify: (item) => item.id,
349+
comparator: {
350+
keyBy: (item) => item.id,
351351
compareBy: (item) => JSON.stringify(item)
352352
}
353353
})
@@ -374,8 +374,8 @@ function MyWidget() {
374374
// When reportFetching == false the object returned from useQuery will only be changed when the data, isLoading or error state changes.
375375
// This method performs a comparison in memory in order to determine changes.
376376
const { data, isLoading } = useQuery(`SELECT * FROM cats WHERE breed = 'tabby'`, [], {
377-
differentiator: {
378-
identify: (item) => item.id,
377+
comparator: {
378+
keyBy: (item) => item.id,
379379
compareBy: (item) => JSON.stringify(item)
380380
}
381381
reportFetching: false

packages/react/src/QueryStore.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ export class QueryStore {
2424
return this.cache.get(key) as WatchedQuery<RowType[]>;
2525
}
2626

27-
const watch = options.differentiator
27+
const watch = options.comparator
2828
? this.db.customQuery(query).differentialWatch({
29-
differentiator: options.differentiator,
29+
comparator: options.comparator,
3030
reportFetching: options.reportFetching,
3131
throttleMs: options.throttleMs
3232
})

0 commit comments

Comments
 (0)