Custom Invoker Commands в React: межкомпонентная связь без контекстов и проп-дриллинга? ⚡️
Нативные Custom Invoker Commands в React: примеры command и onCommand, преимущества и ограничения подхода.

Нативный Invoker Commands API (command и commandfor у <button>) уже активно раскатывается по браузерам. Он позволяет декларативно открывать диалоги или поповеры прямо из HTML без единой строчки JS:
<button command="show-modal" commandfor="my-dialog">Открыть</button>
<dialog id="my-dialog">...</dialog>
️Но в статье Sergio Xalambrí разбирается куда более интересный сценарий — Custom Invoker Commands.
Как это работает в React?
Вместо встроенных команд вроде show-modal мы можем передать кастомную команду (например, command="--custom-action"). Браузер сам отправит кастомное событие command на целевой элемент.
В React мы можем повесить обычный слушатель onCommand на целевой DOM-элемент:
function ActionButton({ targetId }: { targetId: string }) {
return (
<button command="--reset-counter" commandfor={targetId}>
Сбросить счётчик
</button>
);
}
function Counter({ id }: { id: string }) {
const [count, setCount] = useState(0);
return (
<div
id={id}
onCommand={(e) => {
if (e.command === "--reset-counter") {
setCount(0);
}
}}
>
<p>Счётчик: {count}</p>
</div>
);
}
Кнопка и элемент-приёмник могут находиться в абсолютно разных ветках React-дерева — для передачи команды не нужны ни глобальное состояние, ни Context API, ни проп-дриллинг.
➕ Плюсы подхода
- Разрыв зацепления (Decoupling): Кнопка отправки действия вообще ничего не знает про состояние компонента-цели. Ей нужно знать только
idDOM-узла. - Меньше ререндеров: В отличие от React Context или Redux/Zustand, клик по такой кнопке не вызывает каскадный перерендер родительских компонентов или дерева контекста. Работает нативный браузерный Event Dispatching.
- Работает до гидратации (Zero JS на отправку): Нажатие на кнопку генерирует событие на уровне DOM даже в процессе загрузки страницы. Как только React загидрирует целевой компонент и повесит
onCommand, обработчик отработает. - Стандартизация: Это не костыль или специфичный для React паттерн, а нативный специфицированный веб-стандарт.
➖ Минусы и узкие места
- Завязка на ID:
commandforтребует жесткой привязки кidDOM-элемента. В React с компонентами с динамическимid(например, черезuseId()) придётся следить за корректной передачей идентификаторов. - TypeScript & React Types: Так как спецификация свежая, типы React (
ButtonHTMLAttributes) и JSX-событиеonCommandмогут требовать ручного доопределения (declare module 'react') в некоторых проектах. - Строгий синтаксис: Названия кастомных команд обязаны начинаться с двух дефисов (
--my-command), иначе браузер проигнорирует команду или выбросит ошибку.
Как вам идея использовать нативные браузерные эвенты вместо React Context для простых команд в UI?
Также рекомендую прочитать статью про имплементацию Markdown тулбара в React с использованием этого подхода — https://sergiodxa.com/tutorials/build-a-command-driven-markdown-toolbar-in-react