One week ago, I had to implement Rich Text editor as one of features in my side project. After searching, I found draft-js which supported react very well. It looked so cool. So I decided to integrate it with my project. During that integration and studying, I struggled lots - from setting a simple editor to adding more advanced features for it. It was a really amazing journey to acknowledge myself.

Therefore I want to write down that journey in this series.

Let ’s start.

First Phase - Setup simple editor

Goals
Progress

To be quick, I used create-react-app to bootstrap the demo app. I read here to know how to install it

  $ create-react-app editor-by-draftjs
  $ cd editor-by-draftjs

After deps installing finished, I ran below command to make sure It work.

  $ npm run start

The app magically automatically opened browser with http://localhost:3000. At here, I saw the first UI of app

Everything was good until now. I decided to install draft-js

  $ npm i draft-js --save

Then I opened my favorite editor - atom, I created new package at src/Editor/. My editor code was put here.

  src
    | Editor
       - Editor.js
       - index.js

I put below code into Editor.js

  // Editor.js
  /*
    import draft style
  */
  import 'draft-js/dist/Draft.css';
  import React, { Component } from react;
  import {
    Editor,
    EditorState
  } from 'draft-js';

  export default class MyEditor extends Component {
    state = {
      editorState: EditorState.createEmpty()
    }

    handleEditorStateChange = (editorState) =>  {
      this.setState({ editorState });
    }

    render() {
      return (
        <Editor
          editorState={this.state.editorState}
          onChange={this.handleEditorStateChange}
          placeholder="Tell something you like"
        />
      )
    }

  }

Then I exported to src/Editor/index.js

  // index.js
  export { default } from './Editor'

Then I included it into src/App.js

  // src/App.js
  ....
  render() {
    return (
      ....
        <Editor />
      ....
    )
  }
  ....

Tada. Finally, I did it. Simple editor was added.

Goal Completed

  Setup a simple editor by `draft-js` (just for writing)

I wondered how to persist the content to db. Hmm, after reading the documentation, I got 2 methods to do that getCurrentContent & convertToRaw

  rawObj = convertToRaw(editorState.getCurrentContent())

rawObj was a raw javascript object. With it, I could easily to save it to DB. (Btw there were still another ways to do that. I tried to parse it to markup or html before saving it to db by using draft-js-utils.)

But how to parse it again for Editor. Hmm, yah tada, i found convertFromRaw. With this method, I easily constructed the ContentState from rawObj. Then I used EditorState.createWithContent to construct editorState

  editorState = EditorState.createWithContent(convertFromRaw(rawObj))

Goal Completed

   How to get content from editor and send it to DB ?

Hmm It was cool until now. But when I press cmd+b or cmd+i, nothing happened. Hmmm I thought those keys would be automatically bind, but it was not. So I tried to figure out the way to do that.

Tada, draft-js supported us to do it by keyBindingFn and handleKeyCommand. This was draft-js ’s documentation about it.

  // editor.js
  import {
    Editor,
    EditorState,
    RichUtils,
    getDefaultKeyBinding
  } from 'draft-js';

  ...
  /*
  Use RichUtils to tunr on shortkey format like CMD+B, CMD+I
  */
  handleKeyCommand = (command: string, editorState: EditorState): DraftHandleValue => {
    const newState = RichUtils.handleKeyCommand(editorState, command);
    if (newState) {
     this.handleEditorStateChange(newState);
     /*
       handled means we already handle this case - no more work need
     */
     return 'handled';
    }
    /*
     built-in handlers will be triggered if we return not-handleds
    */
    return 'not-handled';
  };
  ...
  render() {
    return (
      <Editor
        editorState={this.state.editorState}
        onChange={this.handleEditorStateChange}
        placeholder="Tell something you like"
        keyBindingFn={getDefaultKeyBinding}
        handleKeyCommand={this.handleKeyCommand}
      />
    )
  }

Goal Completed

   How to add basic keybindings like `cmd+b`, `cmd+i`, ... ?s

So It was all for the part one. Here is the part 2