Перейти к основному содержимому

React компоненты

Пакет @emd-cloud/react-components предоставляет готовые React компоненты и хуки для быстрой интеграции EMD Cloud в ваши React приложения.

Установка

# NPM
npm install @emd-cloud/react-components @emd-cloud/sdk tus-js-client uuid

# Yarn
yarn add @emd-cloud/react-components @emd-cloud/sdk tus-js-client uuid

# PNPM
pnpm add @emd-cloud/react-components @emd-cloud/sdk tus-js-client uuid
Требования

Для работы с чатами требуется @emd-cloud/sdk версии 1.11.0 или выше.

Быстрый старт

1. Оберните приложение в ApplicationProvider

import { ApplicationProvider } from '@emd-cloud/react-components';

function App() {
return (
<ApplicationProvider
app="your-app-id"
apiUrl="https://api.emd.one" // опционально
authToken={localStorage.getItem('auth_token')} // опционально
>
<YourAppComponents />
</ApplicationProvider>
);
}

export default App;

2. Используйте хуки в компонентах

import { useAuth, useDatabase } from '@emd-cloud/react-components';

function UserProfile() {
const { user, login, logout } = useAuth();
const { getRows } = useDatabase('users-collection-id');

if (!user) {
return <LoginForm onLogin={login} />;
}

return (
<div>
<h1>Привет, {user.name}!</h1>
<button onClick={logout}>Выйти</button>
</div>
);
}

ApplicationProvider

Основной компонент, который инициализирует SDK и предоставляет контекст для всех дочерних компонентов.

Props

PropТипОбязательныйОписание
appstringID вашего приложения из EMD Cloud
apiUrlstringURL API endpoint (по умолчанию: https://api.emd.one)
authTokenstringНачальный токен авторизации
tokenTypestringТип токена (по умолчанию: token)
childrenReactNodeДочерние компоненты

Ключевые возможности

  • Автоматическое управление SDK: Инициализирует и управляет экземпляром SDK
  • Динамическая загрузка: Корректно обрабатывает случаи, когда SDK не установлен
  • Управление токенами: Поддерживает состояние авторизации во всем приложении
  • Предоставление контекста: Делает SDK доступным для всех дочерних компонентов

Примеры

Базовая настройка:

import { ApplicationProvider } from '@emd-cloud/react-components';

function App() {
return (
<ApplicationProvider app="your-app-id">
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</Router>
</ApplicationProvider>
);
}

С переменными окружения:

function App() {
return (
<ApplicationProvider
app={process.env.REACT_APP_EMD_CLOUD_APP_ID}
authToken={localStorage.getItem('emd_auth_token')}
>
<YourApp />
</ApplicationProvider>
);
}

С начальным токеном:

function App() {
const [authToken, setAuthToken] = useState(
localStorage.getItem('auth_token')
);

return (
<ApplicationProvider
app="your-app-id"
authToken={authToken}
>
<YourApp onTokenChange={setAuthToken} />
</ApplicationProvider>
);
}

Доступные хуки

Авторизация

  • useAuth — управление авторизацией и профилем пользователя
    • login(), register(), logout()
    • Состояние пользователя и токена
    • Проверка авторизации

База данных

  • useDatabase — CRUD операции с базой данных
    • getRows(), createRow(), updateRow(), deleteRow()
    • Фильтрация и сортировка
    • Автоматическое обновление состояния

Чаты

  • useChat — управление чатами

    • Список каналов и сообщений
    • Отправка сообщений
    • Управление каналами
  • useChatWebSocket — real-time сообщения

    • WebSocket подключение
    • Подписка на каналы
    • Real-time обновления

Загрузка файлов

  • useUploader — загрузка файлов

    • Загрузка с прогрессом
    • Обработка ошибок
    • Множественная загрузка
  • useDropzone — drag & drop интерфейс

    • Drag & drop зона
    • Предпросмотр файлов
    • Валидация

Вебхуки

  • useWebhook — работа с вебхуками
    • Триггер вебхуков
    • Обработка событий

TypeScript поддержка

Все компоненты и хуки полностью типизированы:

import { ApplicationProvider, useAuth, useDatabase } from '@emd-cloud/react-components';
import type { User, DatabaseRow } from '@emd-cloud/sdk';

function UserList() {
const { user } = useAuth();
const { getRows } = useDatabase<DatabaseRow>('users-collection-id');

const [users, setUsers] = useState<DatabaseRow[]>([]);

useEffect(() => {
async function loadUsers() {
const result = await getRows({ limit: 50 });
setUsers(result.data);
}

loadUsers();
}, [getRows]);

return (
<div>
{users.map(user => (
<div key={user._id}>{user.name}</div>
))}
</div>
);
}

Прямой доступ к контексту

Если вам нужен прямой доступ к SDK:

import { useContext } from 'react';
import { ApplicationContext } from '@emd-cloud/react-components';

function CustomComponent() {
const { sdk, user } = useContext(ApplicationContext);

useEffect(() => {
if (sdk) {
// Прямой доступ к SDK
sdk.database('collection-id').getRows({ limit: 10 });
}
}, [sdk]);

return <div>...</div>;
}

Примеры использования

Форма авторизации

import { useAuth } from '@emd-cloud/react-components';
import { useState } from 'react';

function LoginForm() {
const { login, loading, error } = useAuth();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');

const handleSubmit = async (e) => {
e.preventDefault();

try {
await login({ login: email, password });
// Перенаправление после успешного входа
window.location.href = '/dashboard';
} catch (err) {
console.error('Ошибка входа:', err);
}
};

return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Пароль"
required
/>
<button type="submit" disabled={loading}>
{loading ? 'Вход...' : 'Войти'}
</button>
{error && <div className="error">{error.message}</div>}
</form>
);
}

Список данных с фильтрацией

import { useDatabase } from '@emd-cloud/react-components';
import { useState, useEffect } from 'react';

function ProductList() {
const { getRows } = useDatabase('products-collection-id');
const [products, setProducts] = useState([]);
const [filter, setFilter] = useState('all');
const [loading, setLoading] = useState(true);

useEffect(() => {
async function loadProducts() {
setLoading(true);

const query = filter === 'all' ? {} : {
"$and": [{ "data.category": { "$eq": filter } }]
};

const result = await getRows({
query,
limit: 50,
useHumanReadableNames: true
});

setProducts(result.data);
setLoading(false);
}

loadProducts();
}, [filter, getRows]);

if (loading) return <div>Загрузка...</div>;

return (
<div>
<select value={filter} onChange={(e) => setFilter(e.target.value)}>
<option value="all">Все</option>
<option value="electronics">Электроника</option>
<option value="books">Книги</option>
</select>

<div className="products">
{products.map(product => (
<div key={product._id} className="product">
<h3>{product.name}</h3>
<p>{product.price}</p>
</div>
))}
</div>
</div>
);
}

Чат с real-time обновлениями

import { useChat, useChatWebSocket } from '@emd-cloud/react-components';
import { useState, useEffect } from 'react';

function ChatRoom({ channelId }) {
const { sendMessage, listMessages } = useChat();
const { subscribeToChannel, onMessageReceived } = useChatWebSocket();
const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState('');

useEffect(() => {
// Загружаем историю
async function loadMessages() {
const result = await listMessages(channelId, { limit: 50 });
setMessages(result.data);
}

loadMessages();

// Подписываемся на новые сообщения
subscribeToChannel(channelId);

onMessageReceived((message) => {
if (message.channel === channelId) {
setMessages(prev => [...prev, message]);
}
});
}, [channelId]);

const handleSend = async () => {
if (!newMessage.trim()) return;

await sendMessage(channelId, { message: newMessage });
setNewMessage('');
};

return (
<div className="chat-room">
<div className="messages">
{messages.map(msg => (
<div key={msg._id} className="message">
<strong>{msg.user}:</strong> {msg.message}
</div>
))}
</div>

<div className="input">
<input
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSend()}
placeholder="Введите сообщение..."
/>
<button onClick={handleSend}>Отправить</button>
</div>
</div>
);
}

Best Practices

Используйте ApplicationProvider на верхнем уровне

// ✅ Хорошо
function App() {
return (
<ApplicationProvider app="your-app-id">
<Router>
<Routes>...</Routes>
</Router>
</ApplicationProvider>
);
}

// ❌ Плохо: ApplicationProvider внутри роутов
function App() {
return (
<Router>
<ApplicationProvider app="your-app-id">
<Routes>...</Routes>
</ApplicationProvider>
</Router>
);
}

Обрабатывайте состояния загрузки

// ✅ Хорошо
function Component() {
const { user, loading } = useAuth();

if (loading) return <Spinner />;
if (!user) return <LoginForm />;

return <Dashboard user={user} />;
}

Используйте мемоизацию для оптимизации

// ✅ Хорошо
function ProductList() {
const { getRows } = useDatabase('products');

const loadProducts = useCallback(async () => {
const result = await getRows({ limit: 50 });
return result.data;
}, [getRows]);

// ...
}

Следующие шаги

Полезные ссылки