From 7e9d34a38c71893ed94a0d23186276d962aedc2c Mon Sep 17 00:00:00 2001 From: Rohan Ranjan Prasad Date: Sun, 30 Aug 2026 16:42:47 +0530 Subject: [PATCH] feat(typescript): add useRow hook for Vue and React to fix issue #5060 --- crates/bindings-typescript/src/react/index.ts | 1 + .../bindings-typescript/src/react/useRow.ts | 23 ++++++++++++++++ crates/bindings-typescript/src/vue/index.ts | 1 + crates/bindings-typescript/src/vue/useRow.ts | 26 +++++++++++++++++++ 4 files changed, 51 insertions(+) create mode 100644 crates/bindings-typescript/src/react/useRow.ts create mode 100644 crates/bindings-typescript/src/vue/useRow.ts diff --git a/crates/bindings-typescript/src/react/index.ts b/crates/bindings-typescript/src/react/index.ts index 3b3e64aba48..1afa4e4c5b5 100644 --- a/crates/bindings-typescript/src/react/index.ts +++ b/crates/bindings-typescript/src/react/index.ts @@ -1,5 +1,6 @@ export * from './SpacetimeDBProvider.ts'; export { useSpacetimeDB } from './useSpacetimeDB.ts'; export { useTable } from './useTable.ts'; +export { useRow } from './useRow.ts'; export { useReducer } from './useReducer.ts'; export { useProcedure } from './useProcedure.ts'; diff --git a/crates/bindings-typescript/src/react/useRow.ts b/crates/bindings-typescript/src/react/useRow.ts new file mode 100644 index 00000000000..adfd76c750b --- /dev/null +++ b/crates/bindings-typescript/src/react/useRow.ts @@ -0,0 +1,23 @@ +import { useMemo } from 'react'; +import { useTable, type UseTableCallbacks } from './useTable'; +import type { UntypedTableDef, RowType } from '../lib/table'; +import type { Prettify } from '../lib/type_util'; +import type { Query } from '../lib/query'; + +/** + * React hook to subscribe to a view or table in SpacetimeDB and receive a single live row. + * + * This is particularly useful for views that return `t.option(Row)`. + * + * @param query - A query builder expression (table reference or filtered query). + * @param callbacks - Optional callbacks for row insert, delete, and update events. + * @returns A tuple of [row, isReady], where row is `undefined` if not found. + */ +export function useRow( + query: Query, + callbacks?: UseTableCallbacks>> +): [Prettify> | undefined, boolean] { + const [rows, isReady] = useTable(query, callbacks); + const row = useMemo(() => rows[0] ?? undefined, [rows]); + return [row, isReady]; +} diff --git a/crates/bindings-typescript/src/vue/index.ts b/crates/bindings-typescript/src/vue/index.ts index 3b3e64aba48..1afa4e4c5b5 100644 --- a/crates/bindings-typescript/src/vue/index.ts +++ b/crates/bindings-typescript/src/vue/index.ts @@ -1,5 +1,6 @@ export * from './SpacetimeDBProvider.ts'; export { useSpacetimeDB } from './useSpacetimeDB.ts'; export { useTable } from './useTable.ts'; +export { useRow } from './useRow.ts'; export { useReducer } from './useReducer.ts'; export { useProcedure } from './useProcedure.ts'; diff --git a/crates/bindings-typescript/src/vue/useRow.ts b/crates/bindings-typescript/src/vue/useRow.ts new file mode 100644 index 00000000000..1e63f0e5135 --- /dev/null +++ b/crates/bindings-typescript/src/vue/useRow.ts @@ -0,0 +1,26 @@ +import { computed, type Ref, type DeepReadonly } from 'vue'; +import { useTable, type UseTableCallbacks } from './useTable'; +import type { UntypedTableDef, RowType } from '../lib/table'; +import type { Prettify } from '../lib/type_util'; +import type { Query } from '../lib/query'; + +/** + * Vue composable to subscribe to a view or table in SpacetimeDB and receive a single live row. + * + * This is particularly useful for views that return `t.option(Row)`. + * + * @param query - A query builder expression (table reference or filtered query). + * @param callbacks - Optional callbacks for row insert, delete, and update events. + * @returns A tuple of [row, isReady], where row is `undefined` if not found. + */ +export function useRow( + query: Query, + callbacks?: UseTableCallbacks>> +): [ + DeepReadonly> | undefined>>, + DeepReadonly>, +] { + const [rows, isReady] = useTable(query, callbacks); + const row = computed(() => rows.value[0] ?? undefined); + return [row, isReady]; +}