Draft-js: How would I initialize an Editor with some text?

Created on 9 Apr 2016  路  20Comments  路  Source: facebook/draft-js

I want to initialize an Editor with some default text, some with styling (such as bold, H1, H2, etc...) and some plain text. Not sure how to do this. The convertFromRaw requires an array of ContentBlocks, but I'm not sure how to generate the keys for them. Or is there a better way, such as just importing from some html?

Most helpful comment

There are functions to convert from HTML see https://github.com/facebook/draft-js/issues/236

I've used this with success in a previous project.

import React, { PropTypes } from 'react';
import { ContentState, Editor, EditorState, RichUtils, convertToRaw } from 'draft-js';
import DraftPasteProcessor from 'draft-js/lib/DraftPasteProcessor';

export default class MyEditor extends React.Component {

    constructor(props) {
        super(props);
        let editorState;
        if (this.props.content.trim() !== "'') {
            const processedHTML = DraftPasteProcessor.processHTML(this.props.content);
            const contentState = ContentState.createFromBlockArray(processedHTML);
            //move focus to the end. 
            editorState = EditorState.createWithContent(contentState);
            editorState = EditorState.moveFocusToEnd(editorState);
        }
        else {
            editorState = EditorState.createEmpty();
        }

        this.state = {
            editorState: editorState
        };
    }
}

All 20 comments

There are functions to convert from HTML see https://github.com/facebook/draft-js/issues/236

I've used this with success in a previous project.

import React, { PropTypes } from 'react';
import { ContentState, Editor, EditorState, RichUtils, convertToRaw } from 'draft-js';
import DraftPasteProcessor from 'draft-js/lib/DraftPasteProcessor';

export default class MyEditor extends React.Component {

    constructor(props) {
        super(props);
        let editorState;
        if (this.props.content.trim() !== "'') {
            const processedHTML = DraftPasteProcessor.processHTML(this.props.content);
            const contentState = ContentState.createFromBlockArray(processedHTML);
            //move focus to the end. 
            editorState = EditorState.createWithContent(contentState);
            editorState = EditorState.moveFocusToEnd(editorState);
        }
        else {
            editorState = EditorState.createEmpty();
        }

        this.state = {
            editorState: editorState
        };
    }
}

What does this.props.content look like? Is it just a string like so '<div>My content</div>'

Edit: Actually I see the tests done on it in https://github.com/facebook/draft-js/blob/67c5e69499e3b0c149ce83b004872afdf4180463/src/model/paste/__tests__/DraftPasteProcessor-test.js.

Thanks for the helpful info! Hopefully there's more complete documentation on all of Draft's libraries soon.

@brookslyrette Your solution works great on the client side. However, when rendering on the server side, I get an error that document is not defined:

[1] ReferenceError: document is not defined
[1]     at getSafeBodyFromHTML (/Users/mikecho/dev/go/src/remotebase/node_modules/draft-js/lib/getSafeBodyFromHTML.js:27:19)
[1]     at getChunkForHTML (/Users/mikecho/dev/go/src/remotebase/node_modules/draft-js/lib/convertFromHTMLToContentBlocks.js:366:18)
[1]     at convertFromHTMLtoContentBlocks (/Users/mikecho/dev/go/src/remotebase/node_modules/draft-js/lib/convertFromHTMLToContentBlocks.js:422:15)
[1]     at Object.processHTML (/Users/mikecho/dev/go/src/remotebase/node_modules/draft-js/lib/DraftPasteProcessor.js:31:12)
[1]     at new RichEditor (/Users/mikecho/dev/go/src/remotebase/src/components/admin/rich_editor.jsx:13:49)
[1]     at [object Object].ReactCompositeComponentMixin._constructComponentWithoutOwner (/Users/mikecho/dev/go/src/remotebase/node_modules/react/lib/ReactCompositeComponent.js:296:27)
[1]     at [object Object].ReactCompositeComponentMixin._constructComponent (/Users/mikecho/dev/go/src/remotebase/node_modules/react/lib/ReactCompositeComponent.js:278:21)
[1]     at [object Object].ReactCompositeComponentMixin.mountComponent (/Users/mikecho/dev/go/src/remotebase/node_modules/react/lib/ReactCompositeComponent.js:190:21)
[1]     at Object.ReactReconciler.mountComponent (/Users/mikecho/dev/go/src/remotebase/node_modules/react/lib/ReactReconciler.js:47:35)
[1]     at ReactDOMComponent.ReactMultiChild.Mixin.mountChildren (/Users/mikecho/dev/go/src/remotebase/node_modules/react/lib/ReactMultiChild.js:240:44)

using draft-js 0.8.1.

Any suggestions?

@sungwoncho Have you found a solution? I'm having the same issue
(Anyone?)

@theguxi I have not found a solution yet.

type some text and apply your desired styles then get contentState from editor, convertToRaw it to rawContent. then do something like:

contentState = {
                  entityMap: {},
                  blocks: [{
                    key: '18ql9',
                    text: 'your words',
                    type: 'unstyled',
                    depth: 0,
                    inlineStyleRanges: [],
                    entityRanges: [],
                  }],
                };
EditorState.createWithContent(convertFromRaw(contentState))

Have same issue while trying ton render on server side using convertToHTML function. Any update on this issue please.

has this been confirmed for v0.9.1?

Here is a complete boilerplate I ended up with, allowing passing initial and getting html content. You need to install draft-convert. It uses react-draft-wysiwyg, but should work the same with vanilla draft-js.

import React, { Component, PropTypes } from 'react';
import { convertFromHTML, EditorState, ContentState } from 'draft-js';
import { convertToHTML } from 'draft-convert';
import { Editor } from 'react-draft-wysiwyg';

export default class RichTextField extends Component {
  constructor(props) {
    super(props);

    let editorState;

    if (props.content) {
      const blocksFromHTML = convertFromHTML(props.content);
      const contentState = ContentState.createFromBlockArray(blocksFromHTML);
      editorState = EditorState.createWithContent(contentState);
    }
    else {
      editorState = EditorState.createEmpty();
    }

    this.state = { editorState };
  }

  handleChange = editorState => {
    const content = convertToHTML(editorState.getCurrentContent());
    console.log(content);
  }

  render() {
    return (
      <Editor
        defaultEditorState={ this.state.editorState }
        onEditorStateChange={ this.handleChange }
      />
    );
  }
}

RichTextField.propTypes = {
  content: PropTypes.string
};

Thanks a lot for the boilerpalte @jide
How do you get the content to remain in the editor after a refresh?

The content is sucessfully inserted in the DB, I get it back in the console log, but don't know how to keep it in the Editor...

handleChange = editorState => {
  const content = convertToHTML(editorState.getCurrentContent());
  Meteor.call('bins.update', this.props.bin, content);
  console.log(this.props.bin.content);
}

@astenmies I ended up doing something like this, I stripped down to the minimum from my needs, you may need to adapt :

import React, { Component, PropTypes } from 'react';
import { convertFromHTML, EditorState, ContentState } from 'draft-js';
import { convertToHTML } from 'draft-convert';
import { Editor } from 'react-draft-wysiwyg';
import debounce from 'lodash.debounce';

export default class RichTextField extends Component {
  constructor(props) {
    super(props);

    this.state = {
      editorState: this.getInitialStateFromHTMLValue(props.content)
    };
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.content !== convertToHTML(this.state.editorState.getCurrentContent())) {
      this.setState({
        editorState: this.getUpdatedStateFromHTMLValue(nextProps.content)
      });
    }
  }

  componentWillUnmount() {
    this.unmounted = true;
  }

  getInitialStateFromHTMLValue = value => {
    let editorState;

    if (value) {
      const blocksFromHTML = convertFromHTML(value);
      const contentState = ContentState.createFromBlockArray(blocksFromHTML);
      editorState = EditorState.createWithContent(contentState);
      editorState = EditorState.set(editorState);
    }
    else {
      editorState = EditorState.createEmpty();
    }

    return editorState;
  }

  getUpdatedStateFromHTMLValue = value => {
    const { editorState } = this.state;
    const blocksFromHTML = convertFromHTML(value);
    const nextContentState = ContentState.createFromBlockArray(blocksFromHTML);

    return EditorState.push(editorState, nextContentState);
  }

  debouncedOnChange = debounce(this.props.onChange, 100)

  handleChange = editorState => {
    const prevValue = convertToHTML(this.state.editorState.getCurrentContent());
    const nextValue = convertToHTML(editorState.getCurrentContent());

    if (!this.unmounted) {
      this.setState({ editorState }, () => {
        if (prevValue !== nextValue) {
          this.debouncedOnChange(nextValue);
        }
      });
    }
  }

  render() {
    return (
      <Editor
        editorState={ this.state.editorState }
        onEditorStateChange={ this.handleChange }
      />
    );
  }
}

RichTextField.propTypes = {
  content: PropTypes.string,
  onChange: PropTypes.func.isRequired
};

RichTextField.defaultProps = {
  onChange: () => {}
};

Nice @jide. This draft-js is almost the last step of my project and I'm really stuck with this.
By chance do you have any idea why I'm getting a Cannot read property 'trim' of undefined in the inspector, when doing this ... ?

if (!this.unmounted) {
  this.setState({ editorState }, () => {
    if (prevValue !== nextValue) {
      this.debouncedOnChange(nextValue);
    }
  });
  Meteor.call('bins.update', this.props.bin, nextValue);
}

In my example the prop is called "content", whereas it seems you use a "bin" prop.

@jide, I am trying to use the boilerplate you provided, but it says this.props.onChange is not available. Should this function be passed from the parent component?

Additionally, on this line editorState = EditorState.set(editorState); I have the following error
"TypeError: Cannot read property 'decorator' of undefined"

@brookslyrette If the html contain the img, it just find a photo icon , the img can not show normally. Do you have the same problem? or Do you have a solution?

@brookslyrette thanks for the explanation, it worked in my case

@abbycoco Image support was not a required use-case for the work I did. We never tested it.

@abbycoco : Did you find the solution? I had the same problem, found that the problem was with image URL.

Was this page helpful?
0 / 5 - 0 ratings