Socket
The Socket module wraps a socket.io-client connection to the Ovok backend, handles auto-reconnect on network state changes (via @react-native-community/netinfo), and exposes the live Socket instance through React context.
This is the channel real-time SDK features (AI streaming, server-pushed logs, presence) ride on. Most apps only need it if they consume one of those features — REST traffic goes through OvokClient (from @ovok/core), not this socket.
Exports
// from @ovok/native
export { SocketProvider } from "./components/socket-provider";
export { SocketContext } from "./contexts/socket-context";
export type { SocketContextType } from "./types/socket-context-type";
export type { SocketLogPayload } from "./types/socket-log-payload";
export { useAiEventListener } from "./hooks/use-ai-event-listener";
export { useSocket } from "./hooks/use-socket";
Source: src/modules/socket/index.ts.
<SocketProvider>
Wrap your app (inside OvokProvider so the client + access token are available) to open the socket connection.
Props
import type { SocketLogPayload } from "@ovok/native";
type SocketProviderProps = React.PropsWithChildren<{
onError?: (error: Error) => void;
onConnectionError?: (error: Error) => void;
onPing?: (latency: number) => void;
onConnect?: () => void;
onDisconnect?: () => void;
onLogs?: (message: SocketLogPayload) => void;
}>;
| Prop | When it fires |
|---|---|
onConnect | Socket transport opened and authenticated with the bearer token. |
onDisconnect | Socket transport closed (network change, manual disconnect, idle timeout). |
onError | error event from the socket — typically protocol-level failures. |
onConnectionError | connect_error event — the handshake or authentication failed. Both onError and onConnectionError flip internal state back to disconnected. |
onPing | Heartbeat round-trip latency in ms. Useful for surface diagnostics. |
onLogs | Server-pushed log line. SocketLogPayload = { message: string; level: 'info' | 'warn' | 'error' }. |
SocketProvider reads client.getBaseUrl() + client.getAccessToken() from the OvokClient context, so it must be a descendant of OvokProvider. The connection URL is ${baseUrl}events.
Reconnection
SocketProvider registers a NetInfo listener: whenever the device reconnects to the network and the socket is not currently CONNECTED, it tears down the old socket and constructs a new one. When the network goes offline, it actively calls socket.disconnect() so the next reconnect attempt starts from a clean state. Default socket.io-client options: 5 reconnection attempts, 1s → 5s exponential delay.
useSocket()
Read the live socket + connection state from context.
type SocketContextType = {
socket: ReturnType<typeof io> | null;
isConnected: boolean;
};
const { socket, isConnected } = useSocket();
Throws if called outside <SocketProvider>. Returned socket is null until the first connect event resolves; gate any usage on isConnected.
useAiEventListener()
Subscribe to AI events streamed over the socket (currently the /ai/session/message channel).
type MessageEvent = {
content: string;
mode: "user" | "assistant";
reference: string;
sender: string;
sentAt: string;
session: string;
};
const { subscribe } = useAiEventListener();
Use:
import { useAiEventListener } from "@ovok/native";
function ChatView() {
const { subscribe } = useAiEventListener();
const [messages, setMessages] = React.useState<MessageEvent[]>([]);
React.useEffect(() => {
const unsubscribe = subscribe("message", (event) => {
setMessages((prev) => [...prev, event as MessageEvent]);
});
return () => unsubscribe?.();
}, [subscribe]);
return <ChatList messages={messages} />;
}
subscribe(type, callback) returns an unsubscribe function. Multiple subscribers per event type are supported; each receives every event independently.
Full integration example
import { OvokProvider, OvokClient, ExpoClientStorage } from "@ovok/core";
import {
ThemeProvider as OvokThemeProvider,
SocketProvider,
DEFAULT_COLORS,
} from "@ovok/native";
const client = new OvokClient({
baseUrl: "https://api.ovok.com/",
storage: new ExpoClientStorage(),
});
function App() {
return (
<OvokThemeProvider theme={{ colors: DEFAULT_COLORS, dark: false }}>
<OvokProvider client={client}>
<SocketProvider
onConnect={() => console.log("socket up")}
onDisconnect={() => console.log("socket down")}
onConnectionError={(e) => console.error("handshake failed", e)}
onLogs={(payload) => console.log(`[${payload.level}]`, payload.message)}
>
<YourAppRoot />
</SocketProvider>
</OvokProvider>
</OvokThemeProvider>
);
}
When to mount it
- Mount once at the root, inside
OvokProvider. MultipleSocketProviderinstances create multiple sockets, which counts against the backend's per-user connection limit. - Mount only if you consume AI events, server-pushed logs, or another socket-only feature. REST traffic via
useFhirSearch/client.createdoes not need a socket. - Mount before any consumer of
useSocketoruseAiEventListenerin the tree — these throw if called outside.
Related
- OvokProvider documentation — the FHIR client + auth wrapper this depends on