Rich editor for your react typescript project using hooks
Add titles, images and links to your draft.js editor
TL;DR
So you need to give your users the ability to create articles on the fly? Your application is built in react typescript and you don’t use class based components. This tutorial will attempt at showing you exactly how that can be done. I will assume you already spun up a React app and you will be using material-ui for your dom framework/library.
Importing the right stuff.
Draft.js is a very powerful drafting library and with a lot of customisation options however it can be daunting task to try and understand all these functions and how they are used. The library has also changed a bit and v0.10.0 and up works differently from its predecessors. To make matters worse there are a lot of plugins available and getting them to work first time can pose its own set of challenges.
The focus here is to create a static toolbar for your editor that can be used to insert images, headings and links. You can do a lot more with draft.js and for that reason this guide categorises the functionality explained here into it own concerns that you can use however you see fit.
Getting started
The below extract is from the package.json file which will show you what dependencies you will need to install from npm. You can do so in your terminal from your project root using yarn.
Building the editor
Clone draft-js-editor if you would like to follow along from src. The first step is to create your editor element give it access to state as well as setting the focus.
But before we can do that we need to set a context for the editorState in order to have it accessible from outside the react component.
After we have created the context we can move to the editor component and import the types and methods we will need to build the editor.
Our next task is to set the state of the editor and update that state when changes are incurred.
Up to this point we have finished setting up the boilerplate and you should be able to click below the placeholder and start typing.
Building a toolbar
Material ui labs has created a toggle button group component that we will use for our toolbar it is currently in alpha and that is why it needs to be imported from the lab.
in the example project we use the toggle button events and their values passed off to a switch statement that handles the state changes and dom interactions. Let’s do that now.
with modifiers you are able to change content state but before we can do that let’s try and understand how draft.js works.
On a rudimental level editor state is responsible for managing the content state and the content blocks within the content state. When we update the editor state we do so by passing in a new content state which is made of various content blocks, these blocks have access to selection state and block scope amongst a whole host of useful methods. When you stringify a content state you would get a map back that has entities and blocks this map can be converted to raw and converted from raw to allow you to edit the article as well as displaying the article using html.
Modifiers
having a look at the handleToggleButtonGroup switch statement we pass “header-one” if the title button is clicked, then we modify the state and that returns a modified editor state which is bound to the editor state prop on the editor which in turn will make the currently selected block <h1></h1>. It is important to note what methods return; breaking the above example into pieces might help understand this.
const contentState = editorState.getCurrentContent;
const selectionState = editorState.getSelection;
const modifiedContentState = Modifier.setBlockType(contentState,selectionState,'header-one')const newEditorState = EditorState.push(editorState,modifiedContentState,'change-block-type')setEditorState(newEditorState)
The setEditorState is responsible for re-rendering the react app and when updated the Editor component selection or cursor will now type in heading format.
AtomicBlocUtils
Moving to the “insert-image” case we can add the below code to our example which introduces us to atomic blocks and what they are used for.
For the image to display on the editor we need to uncomment the
blockRendererFn={renderBlock}
on the Editor element add the renderBlock into the component.
we also need the media component that tells the editor how to handle the atomic block when encountered.
Whats happening here is when we have an image file we get the current content state from the editor state and insert an immutable/mutable entity. We further update the editorState with the entity using the utility function. When the re-render happens the block render function picks up the entity and displays the block as an image as was defined in the media component.
Draft utils plugins
To insert a link we will be making use of the draft-js-plugins-utils plugin, we will only be using this one plugin and you do need need to install any of the other plugins alongside however you will be required to create a type definition module for the plugin in question.
This is a good time to uncomment the
new CompositeDecorator([ { strategy: linkStrategy, component: DecoratedLink } ]))
that was initialised on the editor state as we will create the needed files for it now.
What we are doing here is tracking the current position of the cursor and when the cursor has a selected range by checking
disabled={editorState.getSelection().isCollapsed()}
you are able to click on the insert link button which will set the toggle to insert a link a the last recorded position from getVisibleSelectionRect that was inside the editor, then using a simple
display: toggleButtonGroupValue === "insert-link" ? "block" : "none"
the text field will display and you can enter the url for your desired link. When clicking on the check icon the program will use the
draftUtils.createLinkAtSelection(editorState, anchorElUrl)
method that has been imported to create the needed content state and modifications and update the editor state, in turn the composite decorator will scan the content of the editor and where it finds the link strategy it will insert your link into that block.