@gulibs/socket.io-react-hooks
v0.0.2
Published
Type-safe Socket.IO React Hooks library with advanced features
Maintainers
Readme
@gulibs/socket.io-react-hooks
类型安全的 Socket.IO React Hooks 库,提供比现有库更多的功能和更好的开发体验。
✨ 特性
- 🎯 完整的 TypeScript 支持 - 完整的类型定义和类型推断
- 🚀 13+ 个实用 Hooks - 覆盖大多数实时通信场景
- 🔌 自动连接管理 - 智能连接共享和清理
- 🌐 SSR 兼容 - 支持服务端渲染
- 📦 Tree-shakeable - 只打包你使用的部分
- ⚡️ 轻量级 - gzipped 仅约 5KB
📦 安装
# npm
npm install @gulibs/socket.io-react-hooks socket.io-client
# yarn
yarn add @gulibs/socket.io-react-hooks socket.io-client
# pnpm
pnpm add @gulibs/socket.io-react-hooks socket.io-client🧪 测试应用
本库包含一个完整的交互式测试应用,用于测试和学习所有 13 个 Hooks 的功能。
启动测试应用
# 1. 确保您的 Socket.IO 服务器正在运行(默认端口 7001)
# 2. 启动测试应用
yarn dev
# 3. 在浏览器中打开 http://localhost:5173配置服务器地址
测试应用默认连接到 http://localhost:7001。您可以通过以下方式配置:
环境变量:
VITE_SOCKET_URL=http://your-server:port yarn dev应用内配置: 在 useSocket 测试页面输入服务器地址
测试功能
- ✅ 13 个 Hook 的独立测试页面 - 每个 Hook 都有详细的测试界面
- ✅ 2 个综合测试场景 - 聊天室和多用户协作
- ✅ 实时状态监控 - 连接状态、延迟、在线用户等
- ✅ 交互式操作 - 通过 UI 直接测试各种功能
- ✅ 完整的使用示例 - 每个页面都包含代码示例
详细的测试说明请参考 TESTING.md。
🚀 快速开始
1. 添加 Provider
import { SocketIOProvider } from '@gulibs/socket.io-react-hooks'
function App() {
return (
<SocketIOProvider defaultOptions={{ url: 'http://localhost:7001' }}>
<YourComponents />
</SocketIOProvider>
)
}SocketIOProvider 属性:
children- React 子组件(必需)defaultOptions?- 默认 Socket.IO 客户端选项(UseSocketOptions,可选)url?: string- Socket.IO 服务器 URLenabled?: boolean- 是否启用连接,默认true- 其他 Socket.IO 客户端选项(如
auth,transports,reconnectionAttempts,path等)
这些默认选项会被所有子组件中的
useSocketHook 继承,子组件可以通过传递自己的选项来覆盖。
2. 使用 Hooks
import { useSocket, useSocketEvent } from '@gulibs/socket.io-react-hooks'
function ChatComponent() {
// 建立连接
const { socket, connected } = useSocket()
// 监听消息
const { lastMessage, sendMessage } = useSocketEvent<string>('message')
if (!connected) {
return <div>连接中...</div>
}
return (
<div>
<p>最新消息: {lastMessage}</p>
<button onClick={() => sendMessage('Hello!')}>
发送消息
</button>
</div>
)
}📚 API 文档
SocketIOProvider
React Context Provider,用于管理所有 Socket.IO 连接。
<SocketIOProvider defaultOptions={options?}>
{children}
</SocketIOProvider>属性(Props):
children- React 子组件(必需,React.ReactNode)defaultOptions?- 默认 Socket.IO 客户端选项(UseSocketOptions,可选)url?: string- Socket.IO 服务器 URL- 示例:
'http://localhost:7001' - 如果为空字符串或未提供,将使用当前页面的 origin
- 示例:
enabled?: boolean- 是否启用连接,默认true- 其他 Socket.IO 客户端选项(继承
ManagerOptions和SocketOptions):auth?: object- 认证信息transports?: Transport[]- 传输方式,如['websocket']reconnectionAttempts?: number- 最大重连次数reconnectionDelay?: number- 重连延迟(毫秒)path?: string- Socket.IO 路径,默认'/socket.io'- 等等。..
说明:
defaultOptions会被所有子组件中的useSocketHook 继承- 子组件可以通过传递自己的选项来覆盖默认选项
- 多个组件使用相同的配置时会共享同一个 Socket 连接实例
- 完全支持 SSR,在服务端会返回安全的 mock 对象
示例:
// 基础用法
<SocketIOProvider>
<App />
</SocketIOProvider>
// 配置默认服务器地址
<SocketIOProvider defaultOptions={{ url: 'http://localhost:7001' }}>
<App />
</SocketIOProvider>
// 配置认证和传输方式
<SocketIOProvider
defaultOptions={{
url: 'https://api.example.com',
auth: { token: 'your-token' },
transports: ['websocket'],
reconnectionAttempts: 5
}}
>
<App />
</SocketIOProvider>
// 配置路径
<SocketIOProvider
defaultOptions={{
url: 'http://localhost:7001',
path: '/custom-socket-path'
}}
>
<App />
</SocketIOProvider>基础 Hooks
useSocket
管理 Socket.IO 连接。
// 函数重载
const { socket, connected, error } = useSocket(options?)
const { socket, connected, error } = useSocket(namespace, options?)
const { socket, connected, error } = useSocket<ServerToClientEvents, ClientToServerEvents>(options?)
const { socket, connected, error } = useSocket<ServerToClientEvents, ClientToServerEvents>(namespace, options?)参数:
namespace?- Socket.IO namespace(字符串,如/chat),默认/options?- Socket.IO 客户端选项(继承ManagerOptions和SocketOptions)url?: string- Socket.IO 服务器 URLenabled?: boolean- 是否启用连接,默认true- 其他 Socket.IO 客户端选项(如
auth,transports,reconnectionAttempts等)
返回:
socket- Socket 实例(SocketLike类型)connected- 连接状态(boolean)error- 错误信息(Error | null)
示例:
// 基础用法
const { socket, connected } = useSocket()
// 指定 namespace
const { socket } = useSocket('/chat')
// 指定 namespace 和选项
const { socket } = useSocket('/chat', {
auth: { token }
})
// 条件连接
const { socket } = useSocket({
enabled: isAuthenticated,
auth: { token }
})
// TypeScript 类型支持
interface ServerToClientEvents {
message: (text: string) => void
}
interface ClientToServerEvents {
sendMessage: (text: string) => void
}
const { socket } = useSocket<ServerToClientEvents, ClientToServerEvents>()useSocketEvent
监听和发送 Socket.IO 事件,支持消息去重和序列号。
// 函数重载
const { socket, status, error, lastMessage, sendMessage } = useSocketEvent(socket, event, options?)
const { socket, status, error, lastMessage, sendMessage } = useSocketEvent(event, options?)参数:
socketOrEvent- Socket 实例或事件名- 如果传入 Socket 实例,则使用该实例
- 如果传入字符串(事件名),则自动创建 Socket 连接
eventOrOptions?- 事件名或配置选项- 当第一个参数是 Socket 时,此参数为事件名(字符串)
- 当第一个参数是事件名时,此参数为配置选项
options?- 配置选项(当第一个参数是 Socket 时使用)onMessage?: (message: T) => void- 收到消息时的回调函数keepPrevious?: boolean- 组件重新挂载时是否保留上一次的消息,默认falsededupe?: boolean | { enabled?: boolean, getId?: (message: T) => string | number, window?: number }- 消息去重配置enabled?: boolean- 是否启用去重,默认truegetId?: (message: T) => string | number- 自定义消息 ID 提取函数,默认使用message.id或message.messageIdwindow?: number- 去重时间窗口(毫秒),默认60000(1 分钟)
sequenceNumber?: boolean | { enabled?: boolean, fieldName?: string | string[], maxWaitTime?: number }- 消息序列号配置enabled?: boolean- 是否启用序列号,默认truefieldName?: string | string[]- 序列号字段名,默认'sequenceNumber'或'seq'或'sequence'maxWaitTime?: number- 最大等待时间(毫秒),默认5000
- 其他
UseSocketOptions选项(如url,enabled等)
返回:
socket- Socket 实例(SocketLike类型)status- 连接状态('connecting' | 'connected' | 'disconnected')error- 错误信息(Error | null)lastMessage- 最新消息(T | undefined)sendMessage- 发送消息函数((...args: any[]) => Promise<any>)- 接受任意数量的参数,最后一个参数可以是回调函数
- 如果提供回调函数,则使用回调;否则返回 Promise
- 支持泛型:
sendMessage<T>(...args) => Promise<T>
示例:
// 使用已有的 socket
const { socket } = useSocket()
const { lastMessage, sendMessage } = useSocketEvent(socket, 'message')
// 不传递 socket,内部自动创建
const { lastMessage, sendMessage } = useSocketEvent('message')
// 带回调
const { lastMessage } = useSocketEvent(socket, 'message', {
onMessage: (msg) => console.log('收到消息:', msg)
})
// 启用消息去重
const { lastMessage } = useSocketEvent('message', {
dedupe: {
enabled: true,
getId: (msg) => msg.id, // 自定义 ID 提取函数
window: 60000 // 去重时间窗口(1分钟)
}
})
// 启用消息序列号(确保消息顺序)
const { lastMessage } = useSocketEvent('message', {
sequenceNumber: {
enabled: true,
fieldName: 'sequenceNumber', // 或 ['seq', 'sequence']
maxWaitTime: 5000 // 最大等待时间(毫秒)
}
})
// 发送带确认的消息
const response = await sendMessage<{ status: string }>('Hello')
console.log(response.status)扩展 Hooks
useSocketConnection
监控连接状态。
const { isConnected, isReconnecting, reconnectAttempts } = useSocketConnection(options?)参数:
options?- Socket.IO 客户端选项(UseSocketOptions)
返回:
isConnected- 是否已连接(boolean)isReconnecting- 是否正在重连(boolean)reconnectAttempts- 重连尝试次数(number)
useSocketReconnection
高级重连控制。
const { reconnect, disconnect, isReconnecting, reconnectAttempts, reconnectError } = useSocketReconnection(options?)参数:
options?- 重连配置选项(UseSocketReconnectionOptions)maxAttempts?: number- 最大重连次数,默认InfinityreconnectionDelay?: number- 重连延迟(毫秒),默认1000- 其他
UseSocketOptions选项(如url,enabled等)
返回:
reconnect- 手动重连函数(() => void)disconnect- 断开连接函数(() => void)isReconnecting- 是否正在重连(boolean)reconnectAttempts- 重连尝试次数(number)reconnectError- 重连错误(Error | null)
useSocketLatency
测量延迟。
const { latency, averageLatency, ping } = useSocketLatency(options?)参数:
options?- 延迟测量配置选项(UseSocketLatencyOptions)interval?: number- Ping 间隔(毫秒),默认5000- 其他
UseSocketOptions选项(如url,enabled等)
返回:
latency- 当前延迟(毫秒,number | null)averageLatency- 平均延迟(毫秒,number | null)ping- 手动发送 ping 函数(() => void)
useSocketTyping
处理"正在输入"指示器。
const { typingUsers, startTyping, stopTyping } = useSocketTyping(event?, options?)参数:
event?- 事件名,默认'typing'options?- 配置选项(UseSocketTypingOptions)timeout?: number- 超时时间(毫秒),超时后自动停止输入状态,默认3000- 其他
UseSocketOptions选项(如url,enabled等)
返回:
typingUsers- 其他用户的输入状态列表(string[])startTyping- 开始输入函数(() => void)stopTyping- 停止输入函数(() => void)
useSocketPresence
用户在线状态管理。
const { onlineUsers, updatePresence } = useSocketPresence(options?)参数:
options?- Socket.IO 客户端选项(UseSocketPresenceOptions,继承UseSocketOptions)
返回:
onlineUsers- 在线用户列表(string[])updatePresence- 更新当前用户的在线状态函数((status: 'online' | 'away' | 'offline') => void)
示例:
// 更新状态
updatePresence('online')
updatePresence('away')
updatePresence('offline')useSocketRoom
房间管理。
const { currentRooms, joinRoom, leaveRoom, leaveAllRooms } = useSocketRoom(options?)参数:
options?- 房间管理配置选项(UseSocketRoomOptions)autoLeaveOnUnmount?: boolean- 组件卸载时是否自动离开房间,默认truejoinEventName?: string- 自定义加入房间事件名,默认'joinRoom'leaveEventName?: string- 自定义离开房间事件名,默认'leaveRoom'- 其他
UseSocketOptions选项(如url,enabled等)
返回:
currentRooms- 当前已加入的房间列表(string[])joinRoom- 加入房间函数((room: string) => Promise<void>)leaveRoom- 离开房间函数((room: string) => Promise<void>)leaveAllRooms- 离开所有房间函数(() => Promise<void>)
示例:
// 加入房间
await joinRoom('room-1')
// 离开房间
await leaveRoom('room-1')
// 离开所有房间
await leaveAllRooms()useSocketBroadcast
广播消息。
const { broadcast, broadcastToRoom } = useSocketBroadcast(options?)参数:
options?- Socket.IO 客户端选项(UseSocketBroadcastOptions,继承UseSocketOptions)
返回:
broadcast- 广播消息到所有用户(除了自己)函数((event: string, data: any) => void)broadcastToRoom- 广播消息到指定房间函数((room: string, event: string, data: any) => void)
示例:
// 广播到所有用户
broadcast('notification', { message: 'Hello everyone!' })
// 广播到特定房间
broadcastToRoom('room-1', 'notification', { message: 'Hello room!' })useSocketAck
带确认的消息,支持并发请求和自动重试。
const {
sendWithAck,
isPending,
ackError,
pendingRequests,
getRequest,
getAllRequests
} = useSocketAck({
timeout?: number, // Default: 5000ms
retry?: boolean | number | {
maxRetries?: number, // Default: 3
strategy?: 'fixed' | 'exponential', // Default: 'exponential'
initialDelay?: number, // Default: 1000ms
maxDelay?: number // Default: 10000ms
}
})参数:
timeout?- 确认超时时间(毫秒),默认 5000retry?- 重试配置false: 不重试(默认)number: 最大重试次数object: 详细重试配置
返回:
sendWithAck- 发送带确认的消息函数(<T = any>(event: string, data: any, requestId?: string) => Promise<T>)event- 事件名data- 消息数据requestId?- 可选的请求 ID,用于跟踪特定请求- 返回 Promise,解析为服务器响应
isPending- 是否有任何请求正在等待(boolean)ackError- 最后一个请求的错误(Error | null)pendingRequests- 所有待处理的请求 ID 数组(string[])getRequest- 获取特定请求的状态((requestId: string) => AckRequest | undefined)- 返回的
AckRequest包含:requestId,event,data,status('pending' | 'success' | 'error'),error?,startTime,retryCount?
- 返回的
getAllRequests- 获取所有请求(包括已完成的)(() => AckRequest[])
示例:
// 基础用法(无重试)
const { sendWithAck, isPending } = useSocketAck({
timeout: 5000
})
// 发送单个请求
const response = await sendWithAck('request', { data: '...' })
// 发送多个并发请求
const [response1, response2] = await Promise.all([
sendWithAck('request1', { data: '...' }, 'req-1'),
sendWithAck('request2', { data: '...' }, 'req-2')
])
// 启用重试(固定延迟)
const { sendWithAck } = useSocketAck({
timeout: 5000,
retry: {
maxRetries: 3,
strategy: 'fixed',
initialDelay: 1000
}
})
// 启用重试(指数退避)
const { sendWithAck } = useSocketAck({
timeout: 5000,
retry: {
maxRetries: 5,
strategy: 'exponential',
initialDelay: 1000,
maxDelay: 10000
}
})
// 简化重试配置(仅指定重试次数)
const { sendWithAck } = useSocketAck({
retry: 3 // 最多重试 3 次,使用默认指数退避策略
})
// 查看所有待处理的请求
console.log(pendingRequests) // ['req-1', 'req-2']
// 获取特定请求的状态
const request = getRequest('req-1')
console.log(request?.status) // 'pending' | 'success' | 'error'
console.log(request?.retryCount) // 重试次数
// 获取所有请求(包括已完成的)
const allRequests = getAllRequests()
allRequests.forEach(req => {
console.log(`${req.requestId}: ${req.status} (retries: ${req.retryCount || 0})`)
})useSocketQueue
离线消息队列,支持持久化存储(localStorage/IndexedDB)。
const { send, queueSize, clearQueue, getQueue } = useSocketQueue({
maxSize?: number, // Default: 100
persistence?: 'none' | 'localStorage' | 'indexedDB' | 'auto', // Default: 'none'
persistenceKey?: string // Default: 'socket.io-queue-'
})参数:
maxSize?- 队列最大大小,默认 100persistence?- 持久化类型'none': 不持久化(仅内存,页面刷新后丢失)'localStorage': 使用 localStorage(适合小数据量,5-10MB)'indexedDB': 使用 IndexedDB(适合大数据量,性能更好)'auto': 自动选择(优先 IndexedDB)
persistenceKey?- 持久化存储键名前缀,默认'socket.io-queue-'
返回:
send- 发送消息函数((event: string, data: any) => void)- 在线时立即发送,离线时自动加入队列
event- 事件名data- 消息数据
queueSize- 当前队列大小(number)clearQueue- 清空队列函数(() => Promise<void>),同时清除持久化存储getQueue- 获取队列内容(() => QueuedMessage[])- 返回的
QueuedMessage包含:event,data,timestamp
- 返回的
示例:
// 基础用法(无持久化)
const { send, queueSize } = useSocketQueue({
maxSize: 100
})
// 使用 localStorage 持久化
const { send, queueSize, clearQueue, getQueue } = useSocketQueue({
maxSize: 50,
persistence: 'localStorage',
persistenceKey: 'my-app-queue-'
})
// 使用 IndexedDB 持久化(推荐)
const { send, queueSize } = useSocketQueue({
maxSize: 1000,
persistence: 'indexedDB'
})
// 自动选择持久化类型
const { send, queueSize } = useSocketQueue({
persistence: 'auto' // 优先使用 IndexedDB,不支持时降级到 localStorage
})
// 发送消息(离线时自动加入队列)
send('message', { text: 'Hello' })
// 页面刷新后,队列会自动从持久化存储恢复
// 连接恢复后,队列中的消息会自动发送
// 获取队列内容
const queue = getQueue()
console.log('队列中的消息:', queue)
// 清空队列(同时清除持久化存储)
clearQueue()持久化说明:
- 启用持久化后,队列会在每次更新时自动保存
- 页面刷新后,队列会自动从持久化存储恢复
- 连接恢复后,队列中的所有消息会自动发送
- 消息发送成功后,队列和持久化存储会自动清空
useSocketError
统一错误处理,支持错误历史上限和自动清理。
const { errors, lastError, clearErrors } = useSocketError({
maxErrors?: number // Default: 50
})参数:
options?- 错误处理配置选项(UseSocketErrorOptions)maxErrors?: number- 最大错误数,默认50。超过后自动移除最旧的错误- 其他
UseSocketOptions选项(如url,enabled等)
返回:
errors- 错误历史数组(SocketErrorData[],按时间倒序)SocketErrorData包含:code(string),message(string),detail?(any),timestamp(number)
lastError- 最后一个错误(SocketErrorData | null)clearErrors- 清除所有错误函数(() => void)
示例:
// 基础用法(默认最多保存 50 个错误)
const { errors, lastError, clearErrors } = useSocketError()
// 自定义错误历史上限
const { errors, lastError } = useSocketError({
maxErrors: 100 // 最多保存 100 个错误
})
// 显示错误提示
{lastError && (
<Alert type="error">
{lastError.message} (Code: {lastError.code})
</Alert>
)}
// 显示错误列表
{errors.map((error, i) => (
<div key={i}>
{new Date(error.timestamp).toLocaleString()}: {error.message}
</div>
))}
// 清除所有错误
<button onClick={clearErrors}>清除错误</button>useSocketMiddleware
中间件支持,包括速率限制。
import { useSocketMiddleware, createRateLimitMiddleware } from '@gulibs/socket.io-react-hooks'
const { emit, addMiddleware, removeMiddleware } = useSocketMiddleware(options?)参数:
options?- 中间件配置选项(UseSocketMiddlewareOptions)middlewares?: SocketMiddleware[]- 初始中间件列表- 其他
UseSocketOptions选项(如url,enabled等)
返回:
emit- 通过中间件链发送消息函数((event: string, data: any) => void)addMiddleware- 添加中间件函数((middleware: SocketMiddleware) => void)removeMiddleware- 移除中间件函数((middleware: SocketMiddleware) => void)
中间件类型:
type SocketMiddleware = (
event: string,
data: any,
next: (event: string, data: any) => void
) => void示例:
// 添加日志中间件
addMiddleware((event, data, next) => {
console.log('发送:', event, data)
next(event, data)
})
// 添加速率限制中间件
const rateLimitMiddleware = createRateLimitMiddleware({
max: 10, // 最多 10 条
window: 60000, // 1 分钟
key: (event, data) => event, // 基于事件名限流
onExceeded: (event, data, remaining, resetTime) => {
console.warn(`Rate limit exceeded for ${event}. Remaining: ${remaining}`)
}
})
addMiddleware(rateLimitMiddleware)
// 发送消息
emit('message', { text: 'Hello' })🔧 TypeScript 支持
完整的类型定义和类型推断:
import { useSocket, useSocketEvent } from '@gulibs/socket.io-react-hooks'
// 定义事件类型
interface ServerToClientEvents {
message: (data: { id: string; text: string }) => void
notification: (text: string) => void
}
interface ClientToServerEvents {
sendMessage: (text: string, callback: (id: string) => void) => void
joinRoom: (room: string) => void
}
// 使用类型
const { socket } = useSocket<ServerToClientEvents, ClientToServerEvents>()
// 自动类型推断
socket.on('message', (data) => {
// data 的类型自动推断为 { id: string; text: string }
console.log(data.text)
})
socket.emit('sendMessage', 'Hello', (id) => {
// id 的类型自动推断为 string
console.log(id)
})🌟 高级用法
认证连接
const useAuthenticatedSocket = (namespace?: string) => {
const [accessToken] = useCookie('jwt')
return useSocket(namespace, {
enabled: !!accessToken,
auth: { token: accessToken }
})
}条件连接
const { socket, connected } = useSocket({
enabled: user?.isAuthenticated,
transports: ['websocket'],
reconnectionAttempts: 5
})自定义事件处理
const [messages, setMessages] = useState([])
const { lastMessage } = useSocketEvent('message', {
onMessage: (msg) => {
setMessages(prev => [...prev, msg])
},
keepPrevious: true
})🎯 服务器端渲染 (SSR)
库完全兼容 SSR,所有 Hook 在服务端都会返回安全的 mock 对象:
// 在服务端不会创建真实连接
const { socket, connected } = useSocket()
// connected 在服务端始终为 false
// socket 返回一个 mock 对象,所有方法都是安全的 no-op
// 所有 Hook 都支持 SSR
const { lastMessage } = useSocketEvent('message') // 服务端安全
const { send } = useSocketQueue() // 服务端安全
const { sendWithAck } = useSocketAck() // 服务端安全SSR 最佳实践:
- 所有 Hook 在 SSR 环境下自动使用 mock,不会报错
- 在开发环境下会显示警告信息(可通过环境变量关闭)
- 客户端 hydration 后自动建立真实连接
- 不需要任何特殊配置,开箱即用
🤝 贡献
欢迎贡献!请查看我们的 贡献指南。
📄 许可
MIT © gulibs
🔗 相关链接
📝 更新日志
查看 CHANGELOG.md 了解版本历史。
