4

I'm trying to update the text from a calling component into the Editor component. I use the props to pass the text from the caller, but when the text change (props is updated in the ContentEditor) the text in the Editor component is not:

Here is the code of the calling component:

<ControlledEditor htmlContent={this.state.validationResultContent}/>

Here is the code of the Controlled Editor:

    export class ControlledEditor extends Component {
    constructor(props) {
        super(props);
        this.state = {editorState: EditorState.createWithText(this.props.htmlContent)};
    }


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

    render() {

        const { editorState } = this.state;

        return (
            <>
                <Container className="mt-5">
                    <Row>
                        <Editor
                            editorState= {editorState}
                            onEditorStateChange={this.onEditorStateChange}
                            wrapperClassName="demo-wrapper"
                            editorClassName="demo-editor"

                        />
                    </Row>
                </Container>
            </>
        );
    }
}

ControlledEditor.propTypes = {
    htmlContent: PropTypes.string
}

thanks for the help

------- UPDATE 1 -------

  • I'm using react-draft-wysiwyg build onto Draft.js
  • text to render is HTML so I updated the code
  • answer from Linda Paiste with componentDidUpdate solves the main problem link

Following the working code (for me):

export class ControlledEditor extends Component {
    constructor(props) {
        super(props);
        this.state = {editorState: EditorState.createEmpty()}
    }

    componentDidUpdate(prevProps) {
        if (this.props.htmlContent !== prevProps.htmlContent) {
            this.setState({
                    editorState: EditorState.createWithContent(ContentState.createFromBlockArray(convertFromHTML(this.props.htmlContent)))
            });
        }
    }

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

    render() {
        const {editorState} = this.state;
        return (
            <>
                <Container className="mt-5">
                    <Row>
                        <Editor
                            editorState={editorState}
                            wrapperClassName="demo-wrapper"
                            editorClassName="demo-editor"
                            onEditorStateChange={this.onEditorStateChange}
                        />
                    </Row>
                </Container>
                <Container className="mt-5">
                    <Row>
                        <div dangerouslySetInnerHTML={{__html: this.props.htmlContent}}/>
                    </Row>
                </Container>
              
            </>
        );
    }
}

ControlledEditor.propTypes = {
    htmlContent: PropTypes.string
}
robob
  • 1,739
  • 4
  • 26
  • 44

1 Answers1

3

Optional: Updating Draft.js Version

Some of the properties that you are using don't seem to be documented. Perhaps they are from a previous version of draft-js? But I'll write this answer based on the current documentation.

The onEditorStateChange prop of Editor should be renamed to onChange.

There is no createFromText on EditorState. This replacement involves two steps:

  1. ContentState can be created from text with the static method ContentState.createFromText.
  2. EditorState can be created from the ContentState with the static method EditorState.createWithContent

So the initial state should be:

this.state = {
  editorState: EditorState.createWithContent(
    ContentState.createFromText(this.props.htmlContent)
  )
};

The Core Problem

With that out of the way, I am able to run your code and reproduce your issue:

I use the props to pass the text from the caller, but when the text change (props is updated in the ContentEditor) the text in the Editor component is not.

You are creating this.state.editorState based on the value of this.props.htmlContent in the constructor. That part of the code only runs one time when the ControlledEditor component first mounts. It does re-run when the props change, so it can't respond to changes in this.props.

You would need to add a componentDidUpdate lifecycle method for that.

componentDidUpdate(prevProps) {
  if (this.props.htmlContent !== prevProps.htmlContent) {
    this.setState({
      editorState: EditorState.createWithContent(
        ContentState.createFromText(this.props.htmlContent)
      )
    });
  }
}

In my opinion, it's easier to convert this to a function component and use hooks.

import { useEffect, useState } from "react";
import { Editor, EditorState, ContentState } from "draft-js";
import "draft-js/dist/Draft.css";

// helper function
const createState = (text) => {
  return EditorState.createWithContent(ContentState.createFromText(text));
};

const ControlledEditor = ({ htmlContent }) => {
  // define the local state, using the createState callback to create the initial value
  const [editorState, setEditorState] = useState(createState(htmlContent));

  // override the local state any time that the props change
  useEffect(() => {
    setEditorState(createState(htmlContent));
  }, [htmlContent]);

  return (
    <Editor
      editorState={editorState}
      onChange={setEditorState}
    />
  );
};

export default function App() {
  const [text, setText] = useState("Hello World");
  return (
    <div>
      <h2>Source Text</h2>
      <textarea value={text} onChange={(e) => setText(e.target.value)} />
      <h2>Editor</h2>
      <ControlledEditor htmlContent={text} />
    </div>
  );
}
Linda Paiste
  • 38,446
  • 6
  • 64
  • 102
  • I use "react-draft-wysiwyg" and using "onChange" instead of "onEditorStateChange" raises this exception: github.com/jpuri/react-draft-wysiwyg/issues/255. So I've reverted to "onEditorStateChange". But using componentDidUpdate solves main problem. I prefer to use Component. I edited the update that works to elaborate the best solution, but It works now! Thanks. I'll give you the right answer. Maybe could you elaborate your answer to manage the exception? – robob Sep 19 '21 at 04:45
  • 1
    It looks like the `react-draft-wysiwyg` package hasn't been updated in a while and it's using an older version of `draft-js`. So stick to `onEditorStateChange`. – Linda Paiste Sep 19 '21 at 05:17