Skip to content

Commit eb149c0

Browse files
Return query collection from useLiveQuery (#74)
Co-authored-by: Sam Willis <sam.willis@gmail.com>
1 parent e5cf287 commit eb149c0

2 files changed

Lines changed: 117 additions & 0 deletions

File tree

packages/react-db/src/useLiveQuery.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from "react"
22
import { useStore } from "@tanstack/react-store"
33
import { compileQuery, queryBuilder } from "@tanstack/db"
44
import type {
5+
Collection,
56
Context,
67
InitialQueryBuilder,
78
QueryBuilder,
@@ -12,6 +13,7 @@ import type {
1213
export interface UseLiveQueryReturn<T extends object> {
1314
state: Map<string, T>
1415
data: Array<T>
16+
collection: Collection<T>
1517
}
1618

1719
export function useLiveQuery<
@@ -50,5 +52,6 @@ export function useLiveQuery<
5052
return {
5153
state,
5254
data,
55+
collection: compiledQuery.results,
5356
}
5457
}

packages/react-db/tests/useLiveQuery.test.tsx

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type Person = {
1717
age: number
1818
email: string
1919
isActive: boolean
20+
team: string
2021
}
2122

2223
type Issue = {
@@ -33,20 +34,23 @@ const initialPersons: Array<Person> = [
3334
age: 30,
3435
email: `john.doe@example.com`,
3536
isActive: true,
37+
team: `team1`,
3638
},
3739
{
3840
id: `2`,
3941
name: `Jane Doe`,
4042
age: 25,
4143
email: `jane.doe@example.com`,
4244
isActive: true,
45+
team: `team2`,
4346
},
4447
{
4548
id: `3`,
4649
name: `John Smith`,
4750
age: 35,
4851
email: `john.smith@example.com`,
4952
isActive: true,
53+
team: `team1`,
5054
},
5155
]
5256

@@ -592,6 +596,116 @@ describe(`Query Collections`, () => {
592596
// Restore console.log
593597
console.log = originalConsoleLog
594598
})
599+
600+
it(`should be able to query a result collection`, async () => {
601+
const emitter = mitt()
602+
603+
// Create collection with mutation capability
604+
const collection = new Collection<Person>({
605+
id: `optimistic-changes-test`,
606+
sync: {
607+
sync: ({ begin, write, commit }) => {
608+
// Listen for sync events
609+
emitter.on(`*`, (_, changes) => {
610+
begin()
611+
;(changes as Array<PendingMutation>).forEach((change) => {
612+
write({
613+
key: change.key,
614+
type: change.type,
615+
value: change.changes as Person,
616+
})
617+
})
618+
commit()
619+
})
620+
},
621+
},
622+
})
623+
624+
// Sync from initial state
625+
act(() => {
626+
emitter.emit(
627+
`sync`,
628+
initialPersons.map((person) => ({
629+
key: person.id,
630+
type: `insert`,
631+
changes: person,
632+
}))
633+
)
634+
})
635+
636+
// Initial query
637+
const { result } = renderHook(() => {
638+
return useLiveQuery((q) =>
639+
q
640+
.from({ collection })
641+
.where(`@age`, `>`, 30)
642+
.keyBy(`@id`)
643+
.select(`@id`, `@name`, `@team`)
644+
.orderBy({ "@id": `asc` })
645+
)
646+
})
647+
648+
// Grouped query derived from initial query
649+
const { result: groupedResult } = renderHook(() => {
650+
return useLiveQuery((q) =>
651+
q
652+
.from({ queryResult: result.current.collection })
653+
.groupBy(`@team`)
654+
.keyBy(`@team`)
655+
.select(`@team`, { count: { COUNT: `@id` } })
656+
)
657+
})
658+
659+
// Verify initial grouped results
660+
expect(groupedResult.current.state.size).toBe(1)
661+
expect(groupedResult.current.state.get(`team1`)).toEqual({
662+
team: `team1`,
663+
count: 1,
664+
})
665+
666+
// Insert two new users in different teams
667+
act(() => {
668+
emitter.emit(`sync`, [
669+
{
670+
key: `5`,
671+
type: `insert`,
672+
changes: {
673+
id: `5`,
674+
name: `Sarah Jones`,
675+
age: 32,
676+
email: `sarah.jones@example.com`,
677+
isActive: true,
678+
team: `team1`,
679+
},
680+
},
681+
{
682+
key: `6`,
683+
type: `insert`,
684+
changes: {
685+
id: `6`,
686+
name: `Mike Wilson`,
687+
age: 38,
688+
email: `mike.wilson@example.com`,
689+
isActive: true,
690+
team: `team2`,
691+
},
692+
},
693+
])
694+
})
695+
696+
await waitForChanges()
697+
698+
// Verify the grouped results include the new team members
699+
expect(groupedResult.current.state.size).toBe(2)
700+
expect(groupedResult.current.state.get(`team1`)).toEqual({
701+
team: `team1`,
702+
count: 2,
703+
})
704+
expect(groupedResult.current.state.get(`team2`)).toEqual({
705+
team: `team2`,
706+
count: 1,
707+
})
708+
})
595709
})
596710

597711
async function waitForChanges(ms = 0) {

0 commit comments

Comments
 (0)