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 -------
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
}
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:
ContentState
can be created from text with the static method ContentState.createFromText
.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)
)
};
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>
);
}