It's fairly cheap, behind the scenes we just add/delete from a Map and a Set. But it's cheaper if you have to do none of this.
useCommandSubscription is an OK abstraction, some (untested) code:
function useCommandSubscription<T>(command: LexicalCommand<T>, fn: CommandListener<T>, priority: CommandListenerPriority): void {
const [editor] = useLexicalComposerContext();
useLayoutEffect(() => {
return editor.registerCommand(command, fn, priority);
}, [editor]);
}
useCommandSubscription(FOO_COMMAND, () => { ... }, priority);
But note how you can further optimize what we provide out of the box:
useEffect(() => {
// You can return immediately, no need to store the cleanup function in a variable
return editor.registerCommand(...);
}, [editor]);
A common use case is that you listen to multiple commands/updates at once, you can leverage mergeRegister (from @lexical/utils):
useEffect(() => {
return mergeRegister(
editor.registerCommand(..),
editor.registerCommand(..),
editor.registerUpdateListener(..),
}, [editor]);
Side note: Beware when listening to the key enter command. Android works with composition and will not trigger a key enter event. Depending on your use case you may want to explore INSERT_LINE_BREAK_COMMAND, INSERT_PARAGRAPH_COMMAND, transforms based on LineBreakNode or ParagraphNode or mutation listeners based on these two nodes.