1

I have this react component that has to abstract draft-js editor. My redux store will have this description field that must be a regular HTML string. The editor should be able to take a value HTML string and parse it into its own internal things (draftjs stuff). When changing the content, it should fire onChange prop with the final HTML content. In other words, it should be transparent to the outside world what's going on inside this component.

Right now, my component looks like this:

import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { Editor, EditorState, ContentState, convertFromHTML } from 'draft-js'
import { stateToHTML } from 'draft-js-export-html'

export const getStateFromHTML = (html) => {
  const blocksFromHTML = html && convertFromHTML(html)
  if (blocksFromHTML) {
    const state = ContentState.createFromBlockArray(
      blocksFromHTML.contentBlocks,
      blocksFromHTML.entityMap,
    )
    return EditorState.createWithContent(state)
  }
  return EditorState.createEmpty()
}

export default class WYSIWYG extends Component {
  static propTypes = {
    value: PropTypes.string,
    onChange: PropTypes.func,    
  }

  static defaultProps = {
    value: '',
    onChange: Function.prototype,
  }

  state = { editorState: null }

  componentWillMount() {
    this.setEditorState(this.props.value)
  }

  componentWillReceiveProps({ value }) {
    this.setEditorState(value)
  }

  onChange = (editorState) => {
    this.setState({ editorState })
    const rawData = stateToHTML(editorState.getCurrentContent())
    this.props.onChange(rawData)
  }

  setEditorState(value) {
    this.setState({ editorState: getStateFromHTML(value) })
  }

  render() {
    return (
      <Editor
        editorState={this.state.editorState}
        onChange={this.onChange}
      />
    )
  }
}

By some reason, this is not working. It seems that getCurrentContent is called before the state is actually updated. I'm not really able to think this through.

I'm open to any [new] ways to handle this situation; I just need to be able to send an HTML value in and get an HTML value out. I also accept indications of other WYSIWYG plugins that will do the job.

David Duran
  • 1,786
  • 1
  • 25
  • 36
enapupe
  • 15,691
  • 3
  • 29
  • 45

1 Answers1

0

setState may be asynchronous. It should be treated as an async operation even though this is not noticed most of the time. It schedules updates to the component local state. This might be the problem you are facing.

One possible solution is to use the setState callback which will be executed once the state update was completed.

  onChange = (editorState) => {
    this.setState(
      { editorState },
      () => {
        const rawData = stateToHTML(editorState.getCurrentContent())
        this.props.onChange(rawData)
      }
    )
  }
VanDanic
  • 432
  • 5
  • 12
  • I know about setState being async and unfortunately this is not the issue.. And I don't think that setState callback means it's updated completely, just means the `state` itself will have all data from previous `setState` calls that haven't been applied yet. Anyway, that's not the issue here. Thanks for your time. – enapupe Jun 30 '17 at 12:48