Перейти к содержанию
Learning Platform
Глоссарий Troubleshooting
Урок 08.05 · 25 мин
Средний
TON ConnectReactSDKHooksTON

TON Connect React SDK

TON Connect React SDK — это готовый набор компонентов и хуков для интеграции кошельков в React-приложения. Использование официального SDK вместо самописных решений экономит недели разработки и гарантирует совместимость со всеми основными кошельками экосистемы TON.

В предыдущих уроках мы использовали отдельные хуки и компоненты SDK. Теперь соберём полную картину: все инструменты @tonconnect/ui-react, их возвращаемые типы, сценарии использования и паттерны.


Обзор SDK

Пакет @tonconnect/ui-react — это React-обёртка над @tonconnect/ui, предоставляющая:

  • Компоненты: TonConnectUIProvider, TonConnectButton
  • Хуки: для управления подключением, отправки транзакций, отслеживания состояния
  • UI-кастомизацию: язык, тема, поведение модальных окон
@tonconnect/ui-react
@tonconnect/ui-react
├── TonConnectUIProvider// контекст-провайдер
├── TonConnectButton// кнопка подключения
├── useTonConnectUI()// управление SDK
├── useTonWallet()// состояние кошелька
├── useTonAddress()// адрес кошелька
└── useIsConnectionRestored()// восстановление сессии

Справочник хуков

useTonConnectUI

Главный хук — предоставляет прямой доступ к экземпляру SDK:

const [tonConnectUI, setOptions] = useTonConnectUI();

Возвращает: [TonConnectUI, (options: TonConnectUiOptions) => void]

Возможности tonConnectUI:

МетодОписание
sendTransaction(tx)Отправить транзакцию через кошелёк
disconnect()Отключить текущий кошелёк
openModal()Открыть модальное окно выбора кошелька
closeModal()Закрыть модальное окно
onStatusChange(cb)Подписка на изменения состояния подключения
setConnectRequestParameters(p)Настроить параметры подключения (ton_proof)
connectedBoolean — подключён ли кошелёк
walletТекущий подключённый кошелёк (или null)
// Пример: программное открытие модального окна
function CustomConnectButton() {
  const [tonConnectUI] = useTonConnectUI();

  return (
    <button onClick={() => tonConnectUI.openModal()}>
      Подключить кошелёк
    </button>
  );
}

useTonWallet

Реактивное состояние подключённого кошелька:

const wallet = useTonWallet();

Возвращает: Wallet | null

// Структура Wallet (упрощённо)
interface Wallet {
  account: {
    address: string;    // raw-адрес (0:ca6e321c...)
    chain: string;      // '-239' (mainnet) или '-3' (testnet)
    walletStateInit: string; // BOC stateInit кошелька
    publicKey?: string; // публичный ключ (hex)
  };
  device: {
    platform: string;   // 'android' | 'ios' | 'linux' | 'windows' | 'mac'
    appName: string;    // 'Tonkeeper' | 'MyTonWallet' | ...
    appVersion: string; // '3.5.0'
  };
  connectItems?: {
    tonProof?: TonProofItem; // результат ton_proof (если запрошен)
  };
}
function WalletDetails() {
  const wallet = useTonWallet();

  if (!wallet) return null;

  return (
    <div>
      <p>Приложение: {wallet.device.appName} {wallet.device.appVersion}</p>
      <p>Платформа: {wallet.device.platform}</p>
      <p>Адрес: {wallet.account.address}</p>
      <p>Сеть: {wallet.account.chain === '-239' ? 'Mainnet' : 'Testnet'}</p>
    </div>
  );
}

useTonAddress

Сокращённый доступ к адресу кошелька:

const address = useTonAddress();        // raw-адрес или ''
const userFriendly = useTonAddress(true); // user-friendly формат или ''

Возвращает: string (пустая строка если не подключён)

function AddressDisplay() {
  const rawAddress = useTonAddress();
  const friendlyAddress = useTonAddress(true);

  if (!rawAddress) return <p>Не подключён</p>;

  return (
    <div>
      <p>Raw: {rawAddress}</p>
      <p>Friendly: {friendlyAddress}</p>
    </div>
  );
}

useIsConnectionRestored

Статус восстановления сессии из localStorage:

const isRestored = useIsConnectionRestored();

Возвращает: boolean

  • false — SDK ещё проверяет localStorage
  • true — проверка завершена (кошелёк может быть подключён или нет)
TIP

Всегда проверяйте useIsConnectionRestored() перед отображением UI, зависящего от кошелька

Без этой проверки пользователь увидит “flash” — кратковременное отображение состояния “не подключён”, которое сменится на “подключён” после восстановления сессии из localStorage.


UI-кастомизация

Изменение языка

const [_, setOptions] = useTonConnectUI();

setOptions({
  language: 'ru', // 'en' | 'ru' | 'zh' | ...
});

Настройка действий

setOptions({
  actionsConfiguration: {
    // Куда перенаправить для просмотра транзакции
    twaReturnUrl: 'https://myapp.com',
  },
});

Настройка через Provider

<TonConnectUIProvider
  manifestUrl="https://myapp.com/tonconnect-manifest.json"
  uiPreferences={{
    theme: 'DARK', // 'DARK' | 'LIGHT' | 'SYSTEM'
  }}
  language="ru"
  actionsConfiguration={{
    twaReturnUrl: 'https://myapp.com',
  }}
>
  <App />
</TonConnectUIProvider>

Паттерны управления состоянием

Паттерн 1: условный рендеринг

function DashboardPage() {
  const wallet = useTonWallet();
  const connectionRestored = useIsConnectionRestored();

  // 1. Ждём восстановления сессии
  if (!connectionRestored) {
    return <LoadingSpinner />;
  }

  // 2. Если не подключён -- показываем страницу входа
  if (!wallet) {
    return <ConnectWalletPage />;
  }

  // 3. Подключён -- показываем дашборд
  return <Dashboard wallet={wallet} />;
}

Паттерн 2: обработка отключения

function useWalletGuard() {
  const [tonConnectUI] = useTonConnectUI();

  useEffect(() => {
    const unsubscribe = tonConnectUI.onStatusChange((wallet) => {
      if (!wallet) {
        // Кошелёк отключён -- очищаем авторизацию
        localStorage.removeItem('auth_token');
        // Перенаправляем на страницу входа
        window.location.href = '/login';
      }
    });

    return () => unsubscribe();
  }, [tonConnectUI]);
}

Паттерн 3: отправка транзакции с обработкой ошибок

function useSendTransaction() {
  const [tonConnectUI] = useTonConnectUI();
  const [sending, setSending] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const send = async (to: string, amount: string) => {
    setSending(true);
    setError(null);

    try {
      const result = await tonConnectUI.sendTransaction({
        validUntil: Math.floor(Date.now() / 1000) + 300,
        messages: [{ address: to, amount }],
      });

      return result.boc;
    } catch (err) {
      if (err instanceof Error) {
        setError(err.message);
      } else {
        setError('Неизвестная ошибка');
      }
      return null;
    } finally {
      setSending(false);
    }
  };

  return { send, sending, error };
}

Полный пример: мини-dApp

Объединим все концепции в одном компоненте:

import {

  TonConnectUIProvider,
  TonConnectButton,
  useTonConnectUI,
  useTonWallet,
  useTonAddress,
  useIsConnectionRestored,
} from '@tonconnect/ui-react';
import { toNano } from '@ton/core';
import { useState } from 'react';

function MiniDApp() {
  const wallet = useTonWallet();
  const address = useTonAddress(true);
  const connectionRestored = useIsConnectionRestored();
  const [tonConnectUI] = useTonConnectUI();
  const [recipient, setRecipient] = useState('');
  const [amount, setAmount] = useState('');
  const [status, setStatus] = useState('');

  if (!connectionRestored) {
    return <p>Загрузка...</p>;
  }

  const handleSend = async () => {
    if (!recipient || !amount) return;

    setStatus('Ожидаем подтверждение в кошельке...');

    try {
      const result = await tonConnectUI.sendTransaction({
        validUntil: Math.floor(Date.now() / 1000) + 300,
        messages: [{
          address: recipient,
          amount: toNano(amount).toString(),
        }],
      });

      setStatus('Транзакция отправлена!');
    } catch (error) {
      setStatus('Ошибка: ' + (error instanceof Error ? error.message : 'отклонено'));
    }
  };

  return (
    <div>
      <header>
        <h1>Mini dApp</h1>
        <TonConnectButton />
      </header>

      {wallet ? (
        <main>
          <p>Подключён: {address}</p>
          <p>Кошелёк: {wallet.device.appName}</p>

          <div>
            <input
              placeholder="Адрес получателя"
              value={recipient}
              onChange={(e) => setRecipient(e.target.value)}
            />
            <input
              placeholder="Сумма (TON)"
              value={amount}
              onChange={(e) => setAmount(e.target.value)}
            />
            <button onClick={handleSend}>Отправить</button>
          </div>

          {status && <p>{status}</p>}
        </main>
      ) : (
        <main>
          <p>Подключите кошелёк для начала работы</p>
        </main>
      )}
    </div>
  );
}

// Точка входа
function App() {
  return (
    <TonConnectUIProvider
      manifestUrl="https://myapp.example.com/tonconnect-manifest.json"
      uiPreferences={{ theme: 'DARK' }}
      language="ru"
    >
      <MiniDApp />
    </TonConnectUIProvider>
  );
}

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

При загрузке страницы SDK асинхронно восстанавливает сессию. Важно правильно обработать этот момент:

function AppWithSessionRestore() {
  const connectionRestored = useIsConnectionRestored();
  const wallet = useTonWallet();
  const [tonConnectUI] = useTonConnectUI();

  useEffect(() => {
    if (!connectionRestored) return; // ещё загружается

    if (wallet) {
      // Сессия восстановлена -- загружаем данные пользователя
      fetchUserData(wallet.account.address);
    } else {
      // Сессия не найдена -- показываем экран входа
      showLoginScreen();
    }
  }, [connectionRestored, wallet]);

  if (!connectionRestored) {
    return <LoadingScreen />;
  }

  return <MainApp />;
}

Сводная таблица хуков

ХукВозвращаетКогда использовать
useTonConnectUI()[TonConnectUI, setOptions]Отправка транзакций, управление подключением, кастомизация
useTonWallet()Wallet | nullИнформация о кошельке: устройство, адрес, сеть, ton_proof
useTonAddress()stringТолько адрес кошелька (raw или user-friendly)
useIsConnectionRestored()booleanПроверка завершения восстановления сессии из localStorage

Итоги модуля M07

В этом модуле мы прошли полный путь от теории протокола до практической интеграции:

  1. Протокол TON Connect 2.0 — bridge-архитектура, SSE, X25519 шифрование
  2. Интеграция кошельков — manifest.json, TonConnectButton, сохранение сессии
  3. Отправка транзакций — sendTransaction, nanoTON, payload, multi-message
  4. Аутентификация ton_proof — off-chain Ed25519 подпись, верификация на бэкенде
  5. React SDK — все хуки, компоненты, паттерны управления состоянием

Для практики интеграции TON Connect в реальном приложении обратитесь к лабораторной работе по созданию Mini App, где вы подключите кошелёк, отправите транзакцию и реализуете аутентификацию.


Частые ошибки

  1. Не оборачивают приложение в TonConnectUIProvider: без провайдера хуки useTonConnectUI и useTonWallet не работают.
  2. Используют несколько экземпляров TonConnectUI: SDK должен инициализироваться один раз; дублирование создаёт конфликты состояния.
  3. Забывают установить manifestUrl: без манифеста кошельки не смогут отобразить информацию о вашем dApp при подключении.
  4. Не обрабатывают состояние loading при восстановлении сессии: при обновлении страницы SDK пытается восстановить подключение, и UI должен это отражать.

Проверка знанийKnowledge check
Какие 4 хука предоставляет @tonconnect/ui-react, и в каких сценариях используется каждый?
ОтветAnswer
1) useTonConnectUI() -- управление SDK: отправка транзакций (sendTransaction), отключение кошелька (disconnect), настройка параметров подключения (ton_proof). 2) useTonWallet() -- получение полной информации о подключённом кошельке: адрес, сеть, устройство, ton_proof результат. 3) useTonAddress() -- сокращённый доступ только к адресу (raw или user-friendly). 4) useIsConnectionRestored() -- проверка завершения восстановления сессии из localStorage, чтобы избежать flash неподключённого состояния.

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5