useChainedCommands
const chain = useChainedCommands();
Parameters
tr
(optional)
A transaction to append chained steps to.
Defaults to a new transaction
Return value
An object containing all the chainable commands available in the editor, an introspection method enabled()
, as well as two chain terminating methods - run()
and tr()
.
Description
This hook exposes all the chainable commands from your chosen extensions (and those built in by default).
Commands allow you to modify editor state.
When a command obtained from useChainedCommands
is called, it returns an object containing other chainable commands. This allows you to form chains of multiple commands.
We are using the terminology "chainable commands" as not all commands are chainable, for instance undo
or redo
cannot be chained. The reasons why are explored in this blog post.
Commands from useChainedCommands
differ from their useCommands
cousins in when they are dispatched. Commands from useCommands
are dispatched immediately - whereas chained commands are not dispatched until you call .run()
on the chain.
Usage
This hook is most commonly used in controlled editor scenarios - where chained commands are the only way to dispatch multiple commands at the same time. This hook can however be used for both controlled and uncontrolled editor scenarios.
By convention, when using this hook, you do not deconstruct the commands returned from the hook. Instead, we keep a reference to the entire chain.
const chain = useChainedCommands();
By convention, we also dispatch the commands with run()
in the same React render phase as when the commands were created. That is to say we run()
the commands in the same callback as we create them.
const handleToggleHeading = useCallback(() => {
chain.toggleHeading({ level: 1 }).focus().run();
}, [toggleHeading]);
A command chain does not have to be one continuous statement - the chain will cache any previous commands, meaning you can break your chain up with conditional statements.
const chain = useChainedCommands();
const handleUpdateHref = useCallback(
(href) => {
chain.focus();
if (href === '') {
chain.removeLink();
} else {
chain.updateLink({ href });
}
chain.run();
},
[chain],
);
Greedy application of commands
Command chains are greedy - when run()
they will try to apply as many commands as they can, even if some commands fail.
For the code samples below, assume we are not within a callout node, and therefore updateCallout
cannot not be applied.
Consider the following chain:
const chain = useChainedCommands();
const handleClick = useCallback(() => {
chain.toggleBold().updateCallout({ type: 'info' }).toggleItalic().run();
}, [chain]);
When this chain is run()
the toggleBold
and toggleItalic
are dispatched, but the second command updateCallout
is not (as we're not within a callout, there is nothing to update).
By default, run()
will try and greedily dispatch as many commands as it can.
To opt out of this greedy behaviour, you can;
Check if an entire command chain is valid by interrogating it's
enabled()
property.const chain = useChainedCommands();
const handleClick = useCallback(() => {
chain.toggleBold().updateCallout({ type: 'info' }).toggleItalic();
// No commands would be dispatched in this scenario (as run is never called), so the chain is preserved.
if (chain.enabled()) {
chain.run();
}
}, [chain]);Choose to exit the chain when a command fails with
exitEarly
. This also destroys the chain.const chain = useChainedCommands();
const handleClick = useCallback(() => {
chain.toggleBold().updateCallout({ type: 'info' }).toggleItalic();
// No commands would be dispatched in this scenario, and chain is destroyed.
chain.run({ exitEarly: true });
}, [chain]);