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]; +}