Open adiachenko opened 8 years ago
Setting a unique property on a parent element works well. Perhaps this could be done internally in the TinyMCE component (some content hash value) to make it behave more inline with what people are used to from using other components.
I'm not sure if it's exactly the correct lifecycle method to use, but if the lib's TinyMCE class method componentWillReceiveProps
is updated to this, I've found that it works:
componentWillReceiveProps(nextProps) {
if (!isEqual(this.props.config, nextProps.config)) {
this._init(nextProps.config, nextProps.content)
}
if (!isEqual(this.props.id, nextProps.id)) {
this.id = nextProps.id
}
// Added
if (!isEqual(this.props.content, nextProps.content)) {
tinymce.EditorManager.get(this.id).setContent(nextProps.content)
}
},
+1 for the fix from @jonathaningram
@jonathaningram, your solution would solve your problem, but would cause an unnecessary "re-render" everytime we change the content of the editor using the UI (i.e. typing) in a normal scenario like this one https://github.com/instructure-react/react-tinymce/blob/master/examples/basic/app.js.
How to reproduce:
editor.on("change"
event, which will fire the onChange
handler we have attached to the component via props (onChange={this.handleEditorChange}
).handleEditorChange
) does a setState
.content={this.state.content}
, and a setState
was just fired, componentWillReceiveProps
hook will execute.nextProps.content
will be different than this.props.content
(this.props.content
was never updated after typing in the editor), setContent
will fire. (Note that this setContent is totally unnecessary)This unnecessary setContent
makes the editor glitchy for an instant, re-positioning the cursor.
How I solved the problem reported in this issue, but avoiding the described glitch at the same time:
comparing nextProps.content
with the actual content of the editor
componentWillReceiveProps(nextProps) {
...
if (!isEqual(tinymce.EditorManager.get(this.id).getContent(), nextProps.content)) {
tinymce.EditorManager.get(this.id).setContent(nextProps.content)
}
},
This way, the extra setContent
call will never fire if the event comes from typing something in the editor's UI.
@albertboada thanks so much for the detail!
I just tried it out and it worked great except for a minor mod that was required for when the editor content was empty to begin with:
// check editor exists before use
const editor = tinymce.EditorManager.get(this.id)
if (editor && !isEqual(editor.getContent(), nextProps.content)) {
tinymce.EditorManager.get(this.id).setContent(nextProps.content)
}
cc @JasonTolliver who proposed #23
Why do we need to check if the editor exists. It does, since it's created in the componentDidMount
.
Plus, it works for me with empty initial content.
I'm probably not understanding the issue!
@albertboada yeah not sure, for me it was undefined :confused: may be that I'm doing something unusual, but I can't see what it would be.
Could I see some code example on how you use the component to get that error message?
Note: I only have to load the page to see this happen, don't even need to type.
So I suppose the componentDidReceiveProps
is called by something before the editor exists.
Here's how I'm using it:
<TinyMCE
{...body}
content={body.value}
config={{
plugins: 'autolink link image lists code',
toolbar: 'undo redo | bold italic | alignleft aligncenter alignright | code',
image_caption: true
}}
onChange={this.handleBodyChange} />
({...body}
is from redux-form). But I also tried removing those props and using a string for content
and it will errors.
<TinyMCE
content={'test'}
config={{
plugins: 'autolink link image lists code',
toolbar: 'undo redo | bold italic | alignleft aligncenter alignright | code',
image_caption: true
}} />
OK so just tried putting it in a really dumb container so no redux-form, and it was fine with no error. I'll have to try and figure out what part of my form component is causing that to break and I'll try to make a minimum repeatable example and link it up.
For the record, found a (terrible) bug in my fix.
If you init the TinyMCE without passing a content={ }
prop, the editor will get emptied everytime you type in it (i.e. everytime the editor.on("change")
event fires) :(
Yeah just noticed it, back to the drawing board!
@albertboada @jonathaningram discovered a small issue with the getContent
fix in that you need to explicitly tell it to get the raw content (e.g. getContent({format: 'raw'})
) otherwise if there is any html etc TinyMCE might decide to strip attributes out and give you some false positives.
As for that nasty ol' bug when not passing a content={ }
prop, it all stems from that getDefaultProps
method setting content
to an empty string. Without it, we can easily test if nextProps.content
is undefined (which means that it was never set or something has gone terribly wrong) and thus not perform the update.
e.g.:
const editor = tinymce.EditorManager.get(this.id);
if (nextProps.content && !isEqual(editor.getContent({format: 'raw'}), nextProps.content)) {
editor.setContent(nextProps.content);
editor.selection.select(editor.getBody(), true);
editor.selection.collapse(false);
}
The undefined
equates to an empty string anyway when rendering the DOM elements and give the added benefit of actually knowing when it's undefined lol
@jonathaningram I haven't noticed the need to check for that editor either, but it could very well be an issue
@JasonTolliver thanks for the raw
trick, I just noticed in my editor that anytime you press enter for a newline the edior was jumping back to the previous line, despite that it did indeed insert the line. The reason was because in my on change handler I didn't use raw
:
handleChange = (e) => {
const val = e.target.getContent({format: 'raw'})
// ...
};
Not sure if it's related or if it's just a TinyMCE thing, but it seems to put an
at the start of newlines too, which is annoying. I found some Tiny config to change it from inserting <p>
s to just inserting <br>
s, but that's not what I need, can't see why it needs to put an
at the start of the line though.
cc @albertboada
how to fixed the bug?
http://archive.tinymce.com/wiki.php/API3:method.tinymce.Editor.setContent
.setContent(content, {format : 'raw'})
Whats going on with this bug? This is a pretty big issue for us because our default value is being loaded async. Is there a way to fix this not internally?
The alternative library do not have a problem: https://github.com/HurricaneJames/react-tinymce-input
If you want to load your default content async this could be usefull
{ !!async-content
? <TinyMCE content={async-content} onChange={ ... } config={{...}} />
: <p>Loading...</p>
}
wait until you get your content, then render the component with this content pro: no more headache con: you do not see the editor until your content is loaded
albertboada's method is useful for me.
@bast44
The alternative library do not have a problem: https://github.com/HurricaneJames/react-tinymce-input
Thank you for recommending this!
I am moving to https://github.com/HurricaneJames/react-tinymce-input as well over this issue.
I've got the same issue. Any plans on fixing this?
@mzabriskie with some guidance I could do a PR to fix it. What do you think?
still have this problem when I refresh my content in redux , has this issue been solved in new version?
it started to work as I expected when I assign a key to TinyMec
import React, {Component} from 'react';
import TinyMCE from 'react-tinymce';
class RichTextEditor extends Component {
handleEditorChange = (e) => {
const {input: {
onChange
}} = this.props;
onChange(e.target.getContent());
}
render() {
const { input } = this.props
return (
<div>
<TinyMCE
key={input.value} // Assign a key here
content={input.value}
config={{
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table contextmenu paste code',
].join(' '),
toolbar: 'undo redo | insert | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image',
menubar: false,
statusbar: false,
height: 250,
}}
onChange={this.handleEditorChange}/>
</div>
);
}
}
RichTextEditor.defaultProps = {
addField: true
};
export default RichTextEditor;
@Aathi 👍 thanks, it works
<div style={{display: inputHtml ? 'block' : 'none'}}>
{inputHtml && <TinyMCE
content={inputHtml}
onChange={this.handleEditorChange}
/>}
</div>
<div style={{display: inputHtml ? 'none' : 'block'}}>
<TinyMCE
content={inputHtml}
onChange={this.handleEditorChange}
/>
</div>
Aathi's solution doesn't work for me. Changing the key seems to refresh the editor. The editor loses focus on every key change. Calling input.onChange
forces a prop change of the value coming from redux.
Ended up switching to react-tinymce-input
. It was quite simple too, just a few property name changes and event args.
opinion: use onEditorChange is not re render. https://codesandbox.io/s/tinymce-react-demo-forked-gg7o8?file=/src/components/editor.js:447-461
it started to work as I expected when I assign a key to TinyMec
Thanks, works
Consider the following piece of code.
TinyMCE will ignore the changes of
content
property and will display only it's value on initial render. I managed to get around this limitation by changing the key property on a parent component from a snippet above (which forces it's re-render) but is there any other way to achieve this without hacking my way through?