refactor(chat, hooks): simplify handleSend and update useDeviceId to use useSyncExternalStore

- Simplified handleSend function in Chat component by removing unnecessary useCallback.
- Refactored useDeviceId hook to utilize useSyncExternalStore for better state management and removed useState and useEffect for device ID retrieval.
- Updated useGateway hook to ensure onMessageRef is set correctly on options change.
- Enhanced useScrollFade hook to properly cancel animation frames on cleanup.
This commit is contained in:
Naiyuan Qing 2026-01-30 23:38:10 +08:00
parent 5ee3176d3a
commit 716bbceaf4
4 changed files with 29 additions and 18 deletions

View file

@ -37,11 +37,11 @@ export function Chat() {
},
})
const handleSend = useCallback((text: string) => {
const handleSend = (text: string) => {
if (!hub?.hubId || !activeAgentId) return
addUserMessage(text, activeAgentId)
send(hub.hubId, "message", { agentId: activeAgentId, content: text })
}, [hub?.hubId, activeAgentId, addUserMessage, send])
}
const filtered = activeAgentId
? messages.filter(m => m.agentId === activeAgentId)

View file

@ -1,19 +1,26 @@
import { useState, useEffect } from "react"
import { useSyncExternalStore } from "react"
import { v7 as uuidv7 } from "uuid"
const STORAGE_KEY = "multica-device-id"
export function useDeviceId(): string {
const [deviceId, setDeviceId] = useState("")
useEffect(() => {
let id = localStorage.getItem(STORAGE_KEY)
if (!id) {
id = uuidv7()
localStorage.setItem(STORAGE_KEY, id)
}
setDeviceId(id)
}, [])
return deviceId
function getSnapshot(): string {
let id = localStorage.getItem(STORAGE_KEY)
if (!id) {
id = uuidv7()
localStorage.setItem(STORAGE_KEY, id)
}
return id
}
function subscribe(cb: () => void) {
window.addEventListener("storage", cb)
return () => window.removeEventListener("storage", cb)
}
function getServerSnapshot(): string {
return ""
}
export function useDeviceId(): string {
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
}

View file

@ -12,7 +12,10 @@ export function useGateway(options?: UseGatewayOptions) {
const [state, setState] = useState<ConnectionState>("disconnected")
const clientRef = useRef<GatewayClient | null>(null)
const onMessageRef = useRef(options?.onMessage)
onMessageRef.current = options?.onMessage
useEffect(() => {
onMessageRef.current = options?.onMessage
})
useEffect(() => {
if (!deviceId) return

View file

@ -38,13 +38,14 @@ export function useScrollFade(
const el = ref.current;
if (!el) return;
update();
const frame = requestAnimationFrame(update);
el.addEventListener("scroll", update, { passive: true });
const ro = new ResizeObserver(update);
ro.observe(el);
return () => {
cancelAnimationFrame(frame);
el.removeEventListener("scroll", update);
ro.disconnect();
};