Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion docs/pages/documentation/realtime/use-realtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,38 @@ You can pass a function for comparing subscription event changes. By default, th

When using your own compare function, you typically want to compare unique values:

```tsx highlight=6
```tsx highlight=7
import { useRealtime } from 'react-supabase'

function Page() {
const [result, reexecute] = useRealtime(
'todos',
{ select: { columns:'id, username' } },
(data, payload) => data.username === payload.username,
)

return ...
}
```

## Initial selection of records

When initializing the component you might need to filter your data appropriately. You can pass the options directly to the `useSelect` hook.

First argument can be either a `string` table name or `useSelect` options with table property.

```tsx highlight=7,8,9,10
import { useRealtime } from 'react-supabase'

function Page() {
const [result, reexecute] = useRealtime(
'todos',
{
select: {
columns: 'id, username, description',
filter: (query) => query.eq('completed', false),
}
},
(data, payload) => data.username === payload.username,
)

Expand Down
11 changes: 8 additions & 3 deletions src/hooks/realtime/use-realtime.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect, useReducer } from 'react'
import { SupabaseRealtimePayload } from '@supabase/supabase-js'

import { UseSelectState, useSelect } from '../data'
import { UseSelectConfig, UseSelectState, useSelect } from '../data'
import { useSubscription } from './use-subscription'

export type UseRealtimeState<Data = any> = Omit<
Expand All @@ -23,6 +23,10 @@ export type UseRealtimeAction<Data = any> =
| { type: 'FETCH'; payload: UseSelectState<Data> }
| { type: 'SUBSCRIPTION'; payload: SupabaseRealtimePayload<Data> }

export type UseRealtimeConfig<Data = any> = {
select?: Omit<UseSelectConfig<Data>, 'pause'>
}

export type UseRealtimeCompareFn<Data = any> = (
data: Data,
payload: Data,
Expand All @@ -32,6 +36,7 @@ type CompareFnDefaultData<Data> = Data & { id: any }

export function useRealtime<Data = any>(
table: string,
config?: UseRealtimeConfig<Data>,
compareFn: UseRealtimeCompareFn<Data> = (a, b) =>
(<CompareFnDefaultData<Data>>a).id ===
(<CompareFnDefaultData<Data>>b).id,
Expand All @@ -41,7 +46,7 @@ export function useRealtime<Data = any>(
'Must specify table or row. Cannot listen for all database changes.',
)

const [result, reexecute] = useSelect<Data>(table)
const [result, reexecute] = useSelect<Data>(table, config?.select)
const [state, dispatch] = useReducer<
React.Reducer<UseRealtimeState<Data>, UseRealtimeAction<Data>>
>(reducer(compareFn), result)
Expand All @@ -58,7 +63,7 @@ export function useRealtime<Data = any>(
}

const reducer =
<Data = any>(compareFn: UseRealtimeCompareFn) =>
<Data = any>(compareFn: UseRealtimeCompareFn<Data>) =>
(
state: UseRealtimeState<Data>,
action: UseRealtimeAction<Data>,
Expand Down
2 changes: 2 additions & 0 deletions test/hooks/realtime/__snapshots__/use-realtime.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
exports[`useRealtime should throw when not inside Provider 1`] = `"No client has been specified using Provider."`;

exports[`useRealtime should throw when trying to listen all database changes 1`] = `"Must specify table or row. Cannot listen for all database changes."`;

exports[`useRealtime should throw when trying to listen all database changes via options 1`] = `"Must specify table or row. Cannot listen for all database changes."`;
14 changes: 14 additions & 0 deletions test/hooks/realtime/use-realtime.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,18 @@ describe('useRealtime', () => {
const { result } = renderHook(() => useRealtime('*'), { wrapper })
expect(() => result.current).toThrowErrorMatchingSnapshot()
})

it('should throw when trying to listen all database changes via options', () => {
const { result } = renderHook(
() =>
useRealtime('*', {
select: {
columns: 'id, username, completed',
filter: (query) => query.eq('completed', false),
},
}),
{ wrapper },
)
expect(() => result.current).toThrowErrorMatchingSnapshot()
})
})