90 lines
2.6 KiB
Markdown
90 lines
2.6 KiB
Markdown
|
|
# 实时功能
|
||
|
|
|
||
|
|
## 监听数据变化
|
||
|
|
|
||
|
|
### 启用表的实时功能
|
||
|
|
**重要**:需要监听数据实时变化的数据表必须先通过以下指令启动 RealTime 功能
|
||
|
|
```sql
|
||
|
|
ALTER PUBLICATION supabase_realtime ADD TABLE public.messages;
|
||
|
|
```
|
||
|
|
|
||
|
|
### 监听变化
|
||
|
|
```typescript
|
||
|
|
import { supabase } from 'src/supabase/client.ts';
|
||
|
|
|
||
|
|
const currentUserId = getCurrentUserId(); // 获取当前用户ID
|
||
|
|
|
||
|
|
const channel = supabase
|
||
|
|
.channel('messages')
|
||
|
|
.on(
|
||
|
|
'postgres_changes',
|
||
|
|
{ event: 'INSERT', schema: 'public', table: 'messages' },
|
||
|
|
(payload) => {
|
||
|
|
const newMessage = payload.new;
|
||
|
|
// 只处理其他用户发送的消息,避免重复显示自己的消息
|
||
|
|
if (newMessage.user_id !== currentUserId) {
|
||
|
|
setMessages(prev => [...prev, newMessage]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
)
|
||
|
|
.subscribe();
|
||
|
|
|
||
|
|
// 发送消息的正确做法
|
||
|
|
const sendMessage = async (content) => {
|
||
|
|
// 1. 立即添加到本地状态(避免延迟)
|
||
|
|
const tempMessage = { id: `temp-${Date.now()}`, content, user_id: currentUserId };
|
||
|
|
setMessages(prev => [...prev, tempMessage]);
|
||
|
|
|
||
|
|
// 2. 发送到数据库
|
||
|
|
const { data, error } = await supabase.from('messages').insert({ content, user_id: currentUserId });
|
||
|
|
if (error) {
|
||
|
|
// 发送失败时移除本地消息
|
||
|
|
setMessages(prev => prev.filter(msg => msg.id !== tempMessage.id));
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// ✅ 清理以防止内存泄漏
|
||
|
|
return () => { channel.unsubscribe(); };
|
||
|
|
```
|
||
|
|
|
||
|
|
**注意事项**
|
||
|
|
- 记得为实时表设置适当的 RLS 策略
|
||
|
|
- 发送消息成功后,立即手动将新消息对象添加到本地 messages 数组,不要依赖 Realtime 订阅来显示自己发送的消息(避免延迟)
|
||
|
|
- Realtime 订阅仅用于接收其他用户发送的消息
|
||
|
|
|
||
|
|
## 用户在线状态
|
||
|
|
|
||
|
|
Supabase 提供实时功能来跟踪用户在线状态并在用户间共享状态。
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const roomOne = supabase.channel('room_01');
|
||
|
|
|
||
|
|
// 同步和跟踪状态
|
||
|
|
roomOne
|
||
|
|
.on('presence', { event: 'sync' }, () => {
|
||
|
|
const newState = roomOne.presenceState();
|
||
|
|
console.log('sync', newState);
|
||
|
|
})
|
||
|
|
.on('presence', { event: 'join' }, ({ key, newPresences }) => {
|
||
|
|
console.log('join', key, newPresences);
|
||
|
|
})
|
||
|
|
.on('presence', { event: 'leave' }, ({ key, leftPresences }) => {
|
||
|
|
console.log('leave', key, leftPresences);
|
||
|
|
})
|
||
|
|
.subscribe();
|
||
|
|
|
||
|
|
// 发送状态
|
||
|
|
const userStatus = {
|
||
|
|
user: 'user-1',
|
||
|
|
online_at: new Date().toISOString(),
|
||
|
|
};
|
||
|
|
|
||
|
|
roomOne.subscribe(async (status) => {
|
||
|
|
if (status !== 'SUBSCRIBED') return;
|
||
|
|
const presenceTrackStatus = await roomOne.track(userStatus);
|
||
|
|
console.log(presenceTrackStatus);
|
||
|
|
});
|
||
|
|
|
||
|
|
// ✅ 清理以防止内存泄漏
|
||
|
|
return () => { roomOne.unsubscribe(); };
|
||
|
|
```
|