React Hooks Deep Dive
RANA provides purpose-built React hooks for AI interactions. This lesson covers all available hooks and best practices for building responsive AI interfaces.
Available Hooks
RANA provides several hooks for different use cases:
useChat- Full chat interface with historyuseCompletion- Single completion requestsuseAgent- Agent interactions with toolsuseStream- Low-level streaming controluseTokenCount- Real-time token counting
useChat Hook
The most commonly used hook for chat interfaces:
import { useChat } from '@rana/react';
function ChatComponent() {
const {
// Message state
messages, // Array of messages
input, // Current input value
setInput, // Update input
// Actions
send, // Send current input
append, // Add message without sending
reload, // Regenerate last response
stop, // Stop streaming
// Status
isLoading, // Request in progress
error, // Error object if any
// Utilities
setMessages, // Override all messages
clearMessages // Clear conversation
} = useChat({
api: '/api/chat',
initialMessages: [],
onFinish: (message) => console.log('Done:', message),
onError: (error) => console.error('Error:', error)
});
return (
<div>
{messages.map(m => (
<div key={m.id} className={m.role}>
{m.content}
</div>
))}
<form onSubmit={(e) => { e.preventDefault(); send(); }}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
disabled={isLoading}
/>
<button type="submit">Send</button>
</form>
</div>
);
}useChat Options
const chat = useChat({
// Required
api: string, // API endpoint
// Initial state
initialMessages?: Message[], // Starting messages
initialInput?: string, // Starting input value
id?: string, // Conversation ID
// Callbacks
onFinish?: (message: Message) => void,
onError?: (error: Error) => void,
onResponse?: (response: Response) => void,
// Request options
headers?: Record<string, string>,
body?: Record<string, any>, // Extra body params
credentials?: RequestCredentials,
// Behavior
sendExtraMessageFields?: boolean,
streamMode?: 'text' | 'sse'
});useCompletion Hook
For single-shot completions without conversation history:
import { useCompletion } from '@rana/react';
function CompletionComponent() {
const {
completion, // Current completion text
input,
setInput,
complete, // Trigger completion
isLoading,
error,
stop
} = useCompletion({
api: '/api/complete'
});
return (
<div>
<textarea
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Enter prompt..."
/>
<button onClick={() => complete(input)}>
Generate
</button>
{completion && (
<div className="result">{completion}</div>
)}
</div>
);
}useAgent Hook
For interactions with agents that use tools:
import { useAgent } from '@rana/react';
function AgentComponent() {
const {
messages,
input,
setInput,
send,
isLoading,
error,
// Agent-specific
toolCalls, // Current tool calls
toolResults, // Results from tools
isExecutingTool, // Tool execution in progress
currentTool // Currently executing tool name
} = useAgent({
api: '/api/agent',
onToolCall: async (toolCall) => {
// Handle tool execution client-side if needed
console.log('Tool called:', toolCall.name);
},
onToolResult: (result) => {
console.log('Tool result:', result);
}
});
return (
<div>
{messages.map(m => (
<div key={m.id}>
<div className={m.role}>{m.content}</div>
{m.toolCalls?.map(tc => (
<div key={tc.id} className="tool-call">
Calling: {tc.name}
</div>
))}
</div>
))}
{isExecutingTool && (
<div className="executing">
Executing {currentTool}...
</div>
)}
<input
value={input}
onChange={(e) => setInput(e.target.value)}
/>
<button onClick={send}>Send</button>
</div>
);
}useStream Hook
Low-level hook for custom streaming scenarios:
import { useStream } from '@rana/react';
function CustomStreamComponent() {
const {
data, // Accumulated stream data
chunks, // Individual chunks
isStreaming,
error,
start, // Start streaming
stop, // Stop streaming
reset // Clear data
} = useStream<string>({
url: '/api/stream',
onChunk: (chunk) => {
console.log('Received chunk:', chunk);
},
onComplete: (fullData) => {
console.log('Stream complete:', fullData);
}
});
return (
<div>
<button onClick={() => start({ prompt: 'Hello' })}>
Start Stream
</button>
{isStreaming && <button onClick={stop}>Stop</button>}
<div>{data}</div>
</div>
);
}useTokenCount Hook
Real-time token counting for input validation:
import { useTokenCount } from '@rana/react';
function TokenAwareInput() {
const [text, setText] = useState('');
const { count, isLoading } = useTokenCount(text, {
model: 'claude-sonnet-4-20250514',
debounce: 300 // Debounce in ms
});
const maxTokens = 4000;
const isOverLimit = count > maxTokens;
return (
<div>
<textarea
value={text}
onChange={(e) => setText(e.target.value)}
className={isOverLimit ? 'error' : ''}
/>
<div className="token-count">
{isLoading ? 'Counting...' : `${count} / ${maxTokens} tokens`}
</div>
{isOverLimit && (
<div className="error">
Input exceeds token limit
</div>
)}
</div>
);
}Combining Hooks
Hooks can be combined for complex interfaces:
function AdvancedChat() {
const chat = useChat({ api: '/api/chat' });
const { count } = useTokenCount(chat.input);
// Calculate total conversation tokens
const conversationTokens = chat.messages.reduce(
(acc, m) => acc + (m.tokenCount || 0),
0
);
return (
<div>
<div className="stats">
Conversation: {conversationTokens} tokens
</div>
{chat.messages.map(m => (
<Message key={m.id} message={m} />
))}
<form onSubmit={(e) => { e.preventDefault(); chat.send(); }}>
<input
value={chat.input}
onChange={(e) => chat.setInput(e.target.value)}
/>
<span>{count} tokens</span>
<button type="submit">Send</button>
</form>
</div>
);
}Optimistic Updates
Show user messages immediately for better UX:
const chat = useChat({
api: '/api/chat',
// Message appears immediately, before API response
experimental_prepareRequestBody: ({ messages }) => {
return { messages };
}
});
// Messages are optimistically added before send completes
// If send fails, the message is removed automaticallyError Handling Patterns
function ChatWithErrorHandling() {
const {
messages,
input,
setInput,
send,
error,
reload // Retry last message
} = useChat({
api: '/api/chat',
onError: (error) => {
// Log to error tracking service
trackError(error);
}
});
return (
<div>
{messages.map(m => <Message key={m.id} message={m} />)}
{error && (
<div className="error-banner">
<p>Something went wrong: {error.message}</p>
<button onClick={reload}>Retry</button>
</div>
)}
<form onSubmit={(e) => { e.preventDefault(); send(); }}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
/>
<button type="submit">Send</button>
</form>
</div>
);
}Best Practices
- Use appropriate hooks - useChat for conversations, useCompletion for single requests
- Handle loading states - Always show loading indicators during API calls
- Implement error handling - Display errors and provide retry options
- Debounce token counting - Prevent excessive API calls during typing
- Clean up on unmount - Hooks handle this automatically, but be aware of in-flight requests
What's Next?
Now that you're comfortable with React hooks, the next lesson covers state management patterns for complex AI applications.