- Updated: March 20, 2026
- 8 min read
Creating a Reusable React Hook for OpenClaw Rating API Edge WebSocket Streams
You can create a reusable React hook called useOpenClawRatings that abstracts the Edge WebSocket connection, automatically handles reconnection, parses rating messages, and plugs into any React state‑management solution.
Creating a Reusable React Hook for OpenClaw Rating API Edge WebSocket Streams
1. Introduction
Real‑time dashboards and rating‑driven UI components are becoming core features of modern SaaS products. A well‑engineered hook not only reduces boilerplate but also guarantees a consistent reconnection strategy across the entire codebase. This guide walks you through building useOpenClawRatings from scratch, integrating it with useState, Redux, or Zustand, and deploying the solution on the OpenClaw hosting guide.
Why reusable hooks matter
- Encapsulation – keep WebSocket logic out of UI components.
- Testability – isolated hook can be unit‑tested with mock sockets.
- Consistency – a single reconnection policy prevents duplicate bugs.
Overview of OpenClaw Rating API Edge WebSocket
The Edge WebSocket endpoint streams JSON payloads such as:
{
"type": "rating",
"id": "movie-123",
"score": 4.7,
"timestamp": "2024-11-02T14:23:11Z"
}Each message represents a live rating update for a specific entity (movie, product, etc.). The connection is secured via wss:// and requires an API key passed as a query parameter.
2. Setting up the WebSocket connection
Required dependencies
Start with a fresh React project (Create‑React‑App, Vite, or Next.js). Install only what you need:
npm install react@^18.2.0If you plan to use Redux or Zustand, add them now:
# Redux
npm install @reduxjs/toolkit react-redux
# Zustand
npm install zustandBasic connection code
Below is a minimal WebSocket wrapper that opens a connection and forwards raw messages to a callback.
class EdgeSocket {
constructor(url, onMessage, onError) {
this.url = url;
this.onMessage = onMessage;
this.onError = onError;
this.socket = null;
this.connect();
}
connect() {
this.socket = new WebSocket(this.url);
this.socket.addEventListener('open', () => console.log('WebSocket opened'));
this.socket.addEventListener('message', e => this.onMessage(e.data));
this.socket.addEventListener('error', this.onError);
this.socket.addEventListener('close', () => {
console.warn('WebSocket closed – attempting reconnection');
setTimeout(() => this.connect(), 2000);
});
}
close() {
this.socket && this.socket.close();
}
}3. Building the useOpenClawRatings hook
Hook signature and return values
The hook returns three items:
- ratings – an array of parsed rating objects.
- status – connection status (
connecting,open,error). - reset – a function to manually reconnect.
Implementation
import { useEffect, useRef, useState, useCallback } from 'react';
/**
* useOpenClawRatings – subscribes to the Edge WebSocket and returns live ratings.
*
* @param {string} apiKey Your OpenClaw API key.
* @param {string} [entity] Optional filter (e.g., "movie", "product").
* @returns {{ratings: Array, status: string, reset: Function}}
*/
export function useOpenClawRatings(apiKey, entity) {
const [ratings, setRatings] = useState([]);
const [status, setStatus] = useState('connecting');
const socketRef = useRef(null);
const reconnectAttempts = useRef(0);
const maxBackoff = 30000; // 30 seconds
const parseMessage = useCallback(data => {
try {
const msg = JSON.parse(data);
if (msg.type === 'rating' && (!entity || msg.id.startsWith(entity))) {
setRatings(prev => [...prev, msg]);
}
} catch (e) {
console.error('Failed to parse rating message', e);
}
}, [entity]);
const connect = useCallback(() => {
const url = `wss://edge.openclaw.io/ratings?apiKey=${apiKey}`;
socketRef.current = new WebSocket(url);
socketRef.current.addEventListener('open', () => {
setStatus('open');
reconnectAttempts.current = 0;
});
socketRef.current.addEventListener('message', e => parseMessage(e.data));
socketRef.current.addEventListener('error', () => {
setStatus('error');
});
socketRef.current.addEventListener('close', () => {
setStatus('connecting');
// Exponential back‑off reconnection
const delay = Math.min(1000 * 2 ** reconnectAttempts.current, maxBackoff);
reconnectAttempts.current += 1;
setTimeout(connect, delay);
});
}, [apiKey, parseMessage]);
// Initial connection
useEffect(() => {
connect();
return () => {
socketRef.current && socketRef.current.close();
};
}, [connect]);
// Manual reset hook
const reset = useCallback(() => {
socketRef.current && socketRef.current.close();
setRatings([]);
connect();
}, [connect]);
return { ratings, status, reset };
}Handling reconnection logic
The hook uses an exponential back‑off strategy (1 s, 2 s, 4 s … up to 30 s). This prevents hammering the Edge server while still providing fast recovery for transient network glitches.
Parsing rating messages
Only messages with type: "rating" are kept. If an entity filter is supplied, the hook discards unrelated IDs, keeping the state lean.
Integration with state‑management patterns
Using useState (default)
The hook already returns a local ratings array managed by useState. Most components can consume it directly.
Redux Toolkit
Dispatch the parsed rating to a slice:
// ratingSlice.js
import { createSlice } from '@reduxjs/toolkit';
export const ratingSlice = createSlice({
name: 'ratings',
initialState: [],
reducers: {
addRating: (state, action) => {
state.push(action.payload);
},
resetRatings: () => []
}
});
export const { addRating, resetRatings } = ratingSlice.actions;
export default ratingSlice.reducer;
// In a component
import { useOpenClawRatings } from './useOpenClawRatings';
import { useDispatch } from 'react-redux';
import { addRating, resetRatings } from './ratingSlice';
export function RatingConsumer({ apiKey }) {
const dispatch = useDispatch();
const { status, reset } = useOpenClawRatings(apiKey);
// Hook inside the slice
useEffect(() => {
const handler = data => dispatch(addRating(data));
// You could modify useOpenClawRatings to accept a custom onMessage
}, [dispatch]);
return {status};
}Zustand
Zustand’s minimal API makes it perfect for real‑time streams:
// store.js
import create from 'zustand';
export const useRatingStore = create(set => ({
ratings: [],
addRating: rating => set(state => ({ ratings: [...state.ratings, rating] })),
reset: () => set({ ratings: [] })
}));
// Component
import { useOpenClawRatings } from './useOpenClawRatings';
import { useRatingStore } from './store';
export function RatingZustand({ apiKey }) {
const addRating = useRatingStore(state => state.addRating);
const { status, reset } = useOpenClawRatings(apiKey);
useEffect(() => {
const handler = data => addRating(data);
// Hook can be extended to accept handler
}, [addRating]);
return WebSocket status: {status}
;
}4. Practical example: Using the hook in a component
Below is a complete functional component that displays live ratings for a movie catalog. It uses the default useState integration, Tailwind styling, and provides a manual reconnect button.
import React from 'react';
import { useOpenClawRatings } from './useOpenClawRatings';
export default function LiveMovieRatings({ apiKey }) {
const { ratings, status, reset } = useOpenClawRatings(apiKey, 'movie');
return (
<div className="max-w-2xl mx-auto p-4">
<h2 className="text-2xl font-semibold mb-4">Live Movie Ratings</h2>
<div className="flex items-center mb-2">
<span className="font-medium mr-2">WebSocket:</span>
<span
className={`
px-2 py-1 rounded ${
status === 'open' ? 'bg-green-200' : 'bg-yellow-200'
}`}>
{status}
</span>
<button
onClick={reset}
className="ml-auto bg-blue-600 text-white px-3 py-1 rounded hover:bg-blue-700 transition">
Reconnect
</button>
</div>
<ul className="space-y-2">
{ratings.slice(-10).reverse().map(rating => (
<li key={rating.id + rating.timestamp} className="p-2 border rounded bg-gray-50">
<strong className="mr-2">{rating.id}</strong>
scored <span className="font-bold text-indigo-600">{rating.score}</span>
<span className="text-sm text-gray-500 ml-2">
({new Date(rating.timestamp).toLocaleTimeString()})
</span>
</li>
))}
</ul>
</div>
);
}
The component renders the most recent ten ratings, updates instantly as new messages arrive, and lets the user force a reconnection if needed.
5. Best‑practice tips
Connection cleanup
- Always close the socket in a
useEffectcleanup to avoid memory leaks. - When a component unmounts, call
socketRef.current?.close().
Error handling & back‑off strategies
- Log errors to a monitoring service (e.g., Sentry) for production visibility.
- Cap the exponential back‑off at a reasonable maximum (30 s works well).
- Consider a jitter algorithm to spread reconnection attempts across clients.
Security considerations
- Never hard‑code the API key; use environment variables or a secure vault.
- Validate incoming JSON against a schema (e.g.,
zod) before updating state. - Prefer
wss://and enforce CORS policies on the Edge server.
Testing the hook
Use jest with mock-socket to simulate WebSocket events. Verify that:
- Ratings are appended correctly.
- Reconnection occurs after a forced close.
- Back‑off delays respect the exponential formula.
Performance & scalability
- Throttle UI updates with
requestAnimationFrameif the stream exceeds 30 msg/s. - Store only the last N messages (e.g., 500) to keep memory usage low.
- Offload heavy parsing to a Web Worker for ultra‑high‑throughput scenarios.
6. Internal reference
For a step‑by‑step deployment of the OpenClaw Rating API on UBOS, follow the OpenClaw hosting guide. The guide explains how to configure environment variables, set up TLS, and monitor WebSocket health from the UBOS dashboard.
When you need a broader AI‑powered backend, explore the Enterprise AI platform by UBOS. It offers built‑in observability for WebSocket services and integrates seamlessly with the Workflow automation studio for alerting on connection failures.
If you prefer a low‑code start, the UBOS templates for quick start include a pre‑configured React project with the useOpenClawRatings hook already wired. Pair it with the AI SEO Analyzer template to automatically generate meta tags for your real‑time rating pages.
For marketing teams, the AI marketing agents can ingest live rating data and trigger personalized campaigns in seconds.
Startups often benefit from the UBOS for startups pricing tier, which includes generous WebSocket bandwidth allowances.
SMBs looking for a cost‑effective solution can check out the UBOS solutions for SMBs, which provide a managed Edge service with automatic reconnection handling.
Finally, if you need a visual editor for rapid prototyping, the Web app editor on UBOS lets you drag‑and‑drop the rating component onto a dashboard without writing a line of code.
7. Conclusion
By encapsulating the Edge WebSocket logic inside useOpenClawRatings, you gain a reusable, testable, and production‑ready building block for any real‑time rating UI. The hook’s reconnection strategy, message parsing, and flexible state‑management integration make it suitable for everything from a single‑page component to a large enterprise dashboard.
Ready to try it? Clone the starter repo, add your OpenClaw API key, and watch live ratings flow instantly. Then, head over to the OpenClaw hosting guide to deploy your solution on UBOS with zero‑downtime scaling.
Have questions or want to share your implementation? Join the UBOS community forum or drop a comment below. Happy coding!
For background on the OpenClaw Rating API launch, see the original news article.