@vira-ui/react
v15.0.1
Published
Vira Framework - React hooks for Vira Reactive Protocol
Maintainers
Readme
@vira-ui/react
React хуки для Vira Reactive Protocol (VRP)
Синхронизация состояния между клиентом и сервером через WebSocket
Установка • Быстрый старт • Документация
🎯 Что это?
@vira-ui/react предоставляет React хуки для работы с Vira Reactive Protocol (VRP) — протоколом для real-time синхронизации состояния между клиентом и сервером через WebSocket.
Основные возможности
- ✅ Автоматическая синхронизация — состояние обновляется при изменениях на сервере
- ✅ Двусторонняя связь — можно отправлять события и обновления на сервер
- ✅ Diff-патчи — обновляются только изменённые части данных
- ✅ Переподключение — автоматическое восстановление соединения
- ✅ TypeScript — полная типизация
📦 Установка
npm install @vira-ui/react @vira-ui/core reactТребования:
- React 18.2.0+
@vira-ui/core^1.0.0- Сервер с поддержкой Vira Reactive Protocol
🚀 Быстрый старт
useViraState
Основной хук для синхронизации состояния:
import { useViraState } from '@vira-ui/react';
interface User {
id: string;
name: string;
email: string;
}
function UserProfile({ userId }: { userId: string }) {
const { data, sendUpdate, sendDiff, isConnected } = useViraState<User>(
`user:${userId}`,
{
initial: { id: userId, name: 'Guest', email: '' },
onOpen: () => console.log('Connected'),
deepMerge: true
}
);
if (!isConnected) return <div>Connecting...</div>;
if (!data) return <div>Loading...</div>;
return (
<div>
<h1>{data.name}</h1>
<p>{data.email}</p>
<button onClick={() => sendDiff({ name: 'New Name' })}>
Update Name
</button>
</div>
);
}📚 Документация
useViraState
Подключается к VRP каналу и синхронизирует состояние.
Параметры:
channel— имя канала (например:"user:123","tasks")options— опции конфигурации
Возвращает:
data— текущее состояниеsendUpdate(payload)— полная замена состоянияsendDiff(patch)— частичное обновление (merge)sendEvent(name, payload)— отправка событияisConnected— статус соединенияisLoading— статус загрузки
Опции:
initial— начальное значениеapiUrl— URL сервера (по умолчанию изVITE_API_URL)authToken— токен авторизацииdeepMerge— глубокое слияние для diff-патчейenableMsgId— поддержка idempotencyonOpen,onClose,onError— колбэки событий соединения
Примеры использования
Список элементов
import { useViraState } from '@vira-ui/react';
interface Task {
id: string;
title: string;
completed: boolean;
}
function TasksList() {
const { data, sendEvent, sendDiff } = useViraState<Task[]>(
'tasks',
{ initial: [] }
);
const toggleTask = (taskId: string) => {
const task = data?.find(t => t.id === taskId);
if (task) {
sendDiff({ [taskId]: { completed: !task.completed } });
}
};
const createTask = (title: string) => {
sendEvent('task.created', {
id: crypto.randomUUID(),
title,
completed: false
});
};
return (
<div>
{data?.map(task => (
<div key={task.id}>
<input
type="checkbox"
checked={task.completed}
onChange={() => toggleTask(task.id)}
/>
<span>{task.title}</span>
</div>
))}
<button onClick={() => createTask('New Task')}>
Add Task
</button>
</div>
);
}Одиночный элемент
function UserDetails({ userId }: { userId: string }) {
const { data, sendDiff, isConnected } = useViraState<User>(
`user:${userId}`,
{
initial: null,
deepMerge: true
}
);
const updateName = (name: string) => {
sendDiff({ name });
};
if (!isConnected) {
return <div>Connecting to server...</div>;
}
if (!data) {
return <div>Loading user...</div>;
}
return (
<div>
<input
value={data.name}
onChange={(e) => updateName(e.target.value)}
/>
<p>Email: {data.email}</p>
</div>
);
}С обработкой ошибок
function DataComponent({ channel }: { channel: string }) {
const { data, sendEvent, isConnected, error } = useViraState(
channel,
{
initial: null,
onError: (err) => {
console.error('VRP Error:', err);
// Можно показать уведомление пользователю
},
onClose: () => {
console.log('Connection closed, reconnecting...');
}
}
);
if (error) {
return <div>Error: {error.message}</div>;
}
if (!isConnected) {
return <div>Reconnecting...</div>;
}
return <div>{/* ... */}</div>;
}С авторизацией
function AuthenticatedComponent() {
const authToken = useAuthToken(); // Ваш хук для получения токена
const { data } = useViraState('protected:data', {
authToken,
onError: (err) => {
if (err.message.includes('unauthorized')) {
// Перенаправить на страницу входа
}
}
});
return <div>{/* ... */}</div>;
}🔄 Паттерны использования
Real-time обновления
function LiveDashboard() {
const { data } = useViraState('dashboard:stats', {
initial: { users: 0, orders: 0 }
});
// Данные автоматически обновляются при изменениях на сервере
return (
<div>
<div>Users: {data?.users}</div>
<div>Orders: {data?.orders}</div>
</div>
);
}Оптимистичные обновления
function OptimisticUpdate() {
const { data, sendDiff } = useViraState('user:123', {
initial: { name: 'John' }
});
const updateName = (newName: string) => {
// Сразу обновляем локально (оптимистично)
sendDiff({ name: newName });
// Сервер подтвердит или откатит изменение
};
return (
<input
value={data?.name}
onChange={(e) => updateName(e.target.value)}
/>
);
}События вместо обновлений
function EventDriven() {
const { sendEvent } = useViraState('tasks', { initial: [] });
const handleComplete = (taskId: string) => {
// Отправляем событие вместо прямого обновления
sendEvent('task.completed', { taskId });
// Сервер обработает событие и обновит состояние
};
return <button onClick={() => handleComplete('123')}>Complete</button>;
}🔗 Интеграция
Обычно используется вместе с:
- @vira-ui/core — базовый фреймворк
- @vira-ui/bindings-react — компоненты с автоматическим связыванием
📖 Примеры
Kanban доска
function KanbanBoard() {
const { data, sendEvent } = useViraState<Column[]>('kanban:board', {
initial: []
});
const moveCard = (cardId: string, fromColumn: string, toColumn: string) => {
sendEvent('card.moved', {
cardId,
fromColumn,
toColumn
});
};
return (
<div className="kanban-board">
{data?.map(column => (
<Column
key={column.id}
column={column}
onMoveCard={moveCard}
/>
))}
</div>
);
}Чат
function ChatRoom({ roomId }: { roomId: string }) {
const { data, sendEvent } = useViraState<Message[]>(
`chat:${roomId}`,
{ initial: [] }
);
const sendMessage = (text: string) => {
sendEvent('message.sent', {
id: crypto.randomUUID(),
text,
timestamp: Date.now()
});
};
return (
<div>
<div className="messages">
{data?.map(msg => (
<Message key={msg.id} message={msg} />
))}
</div>
<MessageInput onSend={sendMessage} />
</div>
);
}🔥 Best Practices
- Используйте типизацию — всегда указывайте тип для
useViraState<T> - Обрабатывайте ошибки — используйте
onErrorдля обработки ошибок соединения - Оптимистичные обновления — используйте
sendDiffдля мгновенного обновления UI - События для действий — используйте
sendEventдля действий, которые должны обрабатываться на сервере
📄 License
MIT
🔗 Связанные пакеты
@vira-ui/core- Базовый фреймворк с VRP клиентом@vira-ui/bindings-react- Компоненты с auto-binding@vira-ui/ui- UI компоненты
