Walkthroughs

Executing Commands

2min
up until now, everything we've learned has been about how to write one off logic for your specific slate editor but one of the most powerful things about slate is that it lets you model your specific rich text "domain" however you'd like, and write less one off code in the previous guides we've written some useful code to handle formatting code blocks and bold marks and we've hooked up the onkeydown handler to invoke that code but we've always done it using the built in editor helpers directly, instead of using "commands" slate lets you augment the built in editor object to handle your own custom rich text commands and you can even use pre packaged "plugins" which add a given set of functionality let's see how this works we'll start with our app from earlier const app = () => { const editor = usememo(() => withreact(createeditor()), \[]) const \[value, setvalue] = usestate(\[ { type 'paragraph', children \[{ text 'a line of text in a paragraph ' }], }, ]) const renderelement = usecallback(props => { switch (props element type) { case 'code' return \<codeelement { props} /> default return \<defaultelement { props} /> } }, \[]) const renderleaf = usecallback(props => { return \<leaf { props} /> }, \[]) return ( \<slate editor={editor} value={value} onchange={value => setvalue(value)}> \<editable renderelement={renderelement} renderleaf={renderleaf} onkeydown={event => { if (!event ctrlkey) { return } switch (event key) { case '`' { event preventdefault() const \[match] = editor nodes(editor, { match n => n type === 'code', }) transforms setnodes( editor, { type match ? null 'code' }, { match n => editor isblock(editor, n) } ) break } case 'b' { event preventdefault() transforms setnodes( editor, { bold true }, { match n => text istext(n), split true } ) break } } }} /> \</slate> ) } it has the concept of "code blocks" and "bold formatting" but these things are all defined in one off cases inside the onkeydown handler if you wanted to reuse that logic elsewhere you'd need to extract it we can instead implement these domain specific concepts by creating custom helper functions // define our own custom set of helpers const customeditor = { isboldmarkactive(editor) { const \[match] = editor nodes(editor, { match n => n bold === true, universal true, }) return !!match }, iscodeblockactive(editor) { const \[match] = editor nodes(editor, { match n => n type === 'code', }) return !!match }, toggleboldmark(editor) { const isactive = customeditor isboldmarkactive(editor) transforms setnodes( editor, { bold isactive ? null true }, { match n => text istext(n), split true } ) }, togglecodeblock(editor) { const isactive = customeditor iscodeblockactive(editor) transforms setnodes( editor, { type isactive ? null 'code' }, { match n => editor isblock(editor, n) } ) }, } const app = () => { const editor = usememo(() => withreact(createeditor()), \[]) const \[value, setvalue] = usestate(\[ { type 'paragraph', children \[{ text 'a line of text in a paragraph ' }], }, ]) const renderelement = usecallback(props => { switch (props element type) { case 'code' return \<codeelement { props} /> default return \<defaultelement { props} /> } }, \[]) const renderleaf = usecallback(props => { return \<leaf { props} /> }, \[]) return ( \<slate editor={editor} value={value} onchange={value => setvalue(value)}> \<editable renderelement={renderelement} renderleaf={renderleaf} onkeydown={event => { if (!event ctrlkey) { return } // replace the `onkeydown` logic with our new commands switch (event key) { case '`' { event preventdefault() customeditor togglecodeblock(editor) break } case 'b' { event preventdefault() customeditor toggleboldmark(editor) break } } }} /> \</slate> ) } now our commands are clearly defined and you can invoke them from anywhere we have access to our editor object for example, from hypothetical toolbar buttons const app = () => { const editor = usememo(() => withreact(createeditor()), \[]) const \[value, setvalue] = usestate(\[ { type 'paragraph', children \[{ text 'a line of text in a paragraph ' }], }, ]) const renderelement = usecallback(props => { switch (props element type) { case 'code' return \<codeelement { props} /> default return \<defaultelement { props} /> } }, \[]) const renderleaf = usecallback(props => { return \<leaf { props} /> }, \[]) return ( // add a toolbar with buttons that call the same methods \<slate editor={editor} value={value} onchange={value => setvalue(value)}> \<div> \<button onmousedown={event => { event preventdefault() customeditor toggleboldmark(editor) }} \> bold \</button> \<button onmousedown={event => { event preventdefault() customeditor togglecodeblock(editor) }} \> code block \</button> \</div> \<editable editor={editor} renderelement={renderelement} renderleaf={renderleaf} onkeydown={event => { if (!event ctrlkey) { return } switch (event key) { case '`' { event preventdefault() customeditor togglecodeblock(editor) break } case 'b' { event preventdefault() customeditor toggleboldmark(editor) break } } }} /> \</slate> ) } that's the benefit of extracting the logic and there you have it! we just added a ton of functionality to the editor with very little work and we can keep all of our command logic tested and isolated in a single place, making the code easier to maintain
🤔
Have a question?
Our super-smart AI,knowledgeable support team and an awesome community will get you an answer in a flash.
To ask a question or participate in discussions, you'll need to authenticate first.