PlatformContext
Всё, что ваш модуль знает о платформе, приходит через один объект: PlatformContext. Оболочка внедряет его; вы используете его через React-хуки.
Форма
interface PlatformContext {
user: PlatformUser; // кто вошёл + его роли в realm
organization: PlatformOrganization; // арендатор + его лицензированные модули
building: PlatformBuilding | null; // активное здание — null на уровне организации
locale: string; // "en", "ru", "hi"
theme: 'light' | 'dark';
api: PlatformApiClient; // предварительно аутентифицированный HTTP-клиент в области арендатора
eventBus: EventBusClient; // публикация / подписка на события платформы
}Обратите внимание: building может быть null — пользователь может находиться на уровне организации, не выбрав здание. Используйте useBuilding(), когда маршрут действительно требует здания, и useOptionalBuilding(), когда нет.
Поверхность намеренно узкая: идентичность, арендатор, выбор здания, локаль и тема, два типизированных клиента — вот и весь контракт. Всё, до чего вы дотягиваетесь помимо него, находится вне поддержки платформы и может измениться без предупреждения.
Хуки
import {
usePlatformContext,
useBuilding,
useCurrentUser,
useOptionalBuilding,
} from '@tv/extension-sdk/react';
function MyComponent() {
const ctx = usePlatformContext(); // весь контекст; бросает исключение вне провайдера
const building = useBuilding(); // бросает исключение, если здание не выбрано
const user = useCurrentUser(); // вошедший пользователь
const maybe = useOptionalBuilding(); // null вместо исключения
}Ререндеры — сколько стоит подписка
PlatformContext — настоящий React-контекст, поэтому применимо стандартное опасение: при смене значения провайдера перерисовываются все подписчики. Здесь оно ограничено самой конструкцией:
- Identity значения стабильна — оболочка держит один объект контекста и заменяет его только при смене вошедшего пользователя, выбранного здания, локали или темы. Всё это редкие, инициированные пользователем моменты; первые три и так обесценивают всё, что модуль отрисовал.
- Переключение темы — точечный патч, а не пересборка. Оболочка обновляет поле
themeв объекте контекста, сохраняя всё остальное: подписчики перерисовываются один раз с новым значением, соединения не рвутся. Модулям, которые красятся через CSS-переменные,ctx.themeвообще не нужен; читайте его только для поверхностей, рисуемых из JS (canvas, материалы Three.js). - Высокочастотные данные через значение контекста не текут. Телеметрия и события платформы приходят колбэками
eventBus.subscribe()— шквал событий перерисует только компоненты, чьё состояние вы сами обновили в обработчике, а не всех подписчиков контекста. apiиeventBusсохраняют identity при патче темы и заменяются только при полной пересборке, поэтому указывать их в зависимостях хуков (как в примерах раздела События) корректно и ничего не «дёргает».
Если профилировщик показывает шторм ререндеров в вашем модуле, причина — в вашем собственном управлении состоянием ниже обработчика, а не в контексте.
Клиент api
ctx.api — это HTTP-клиент, который уже несёт аутентификацию пользователя и ограничен областью активного арендатора. Вы никогда не видите токен.
const spaces = await ctx.api.get<Space[]>(`/api/v1/buildings/${building.id}/spaces`);
await ctx.api.post(`/api/v1/buildings/${building.id}/work-orders`, dto);Доступны get / post / put / patch / delete, каждый принимает необязательный { headers, query, signal }.
Почему это важно: один и тот же компонент работает и в вашей песочнице, и в продакшен-арендаторе клиента, потому что единственное, что между ними меняется — аутентификация и базовый URL — поставляет платформа, а не ваш код.
Чтение данных арендатора
ctx.organization.activeModuleIds показывает, на какие модули лицензирован арендатор. Используйте это для мягкой деградации UI, а не как средство безопасности: лицензирование обеспечивается на сервере через @RequiresLicense(), а проверка на клиенте — удобство, а не граница.
const { organization } = usePlatformContext();
const hasCafm = organization.activeModuleIds?.includes('@tv/module-cafm');Не обходите его стороной
Контракт таков: PlatformContext — ваша единственная дверь. Если вы замечаете, что читаете localStorage, собираете URL Keycloak или жёстко прописываете https://tv-api... — остановитесь: этот путь не переживёт переход между арендаторами и находится вне того, что поддерживает платформа.
→ Далее: События