Correct Usage working with Formik for CKEditor
Hi, I'm using Formik with CKeditor as a field. I ran into an issue where typing gets slow and delay when the text content is large (characters around 9K). Looking at some examples, I think my usage may be wrong, but I'm not sure what's the right way, and I don't find much information online on how Formik and CKEditor works together.
What I have currently:
<Field
name={this.props.fieldName}
render={({ field, form }: FieldProps<number | string>) => {
return (
<>
<CKEditor
editor={BalloonEditor}
data={field.value}
onChange={(event: any, editor: any) => {
form.setFieldValue(field.name, editor.getData());
}}
onSaveKeystroke={this.props.onSaveKeystroke}
/>
</>
)
}}
/>
As you can see I'm treating CKEditor as a simple input element, which is capturing every onChange, and from there I getData() and assign new state. Then, the new state will be pour back to CKEditor by data={field.value}. Just in case not everyone is using Formik - the field's state is handled as follow: Formik's form.setFieldValue() works similar to setState(), and the state/new state of that field can be accessed by field.value.
There're two questions I have here that relates to solving the slow issue. First on ensuring the character shows up in the editor as I type - correct me if I'm wrong - I shouldn't do setState on every onChange here, is that right? I now realize that CKEditor is internally calling setData() and maintaining the content state itself, and I think I read somewhere in doc saying CKEditor should not be rerendered/remount by React when data changes as user types. So this is pretty different from a input element. Therefore, we should not maintain a separate state for the editor data, the editor will display the updated content itself w/o supplying new state from prop data=..., is this understanding correct?
The second question I have is, if my understanding is correct in 1st question, I should treat data={...} as just the initial editor data, not the up-to-date data. And instead of calling form.setFieldValue() for every onChange, I should either debounce it, or just do it all once by calling editor.getData() in the form submit logic. Does the rationale here seem right?
If both the understanding above is correct, there're several concerns - if I do some kind of debounce in onChange to limit the times calling form.setFieldValue() (not sure how to do this, still trying to figure out), Formik will update field.value accordingly, where the data=... prop will change. Will this cause problem in <CKEditor /> and trigger unnecessary update/rerendering?
I'd be great if someone already has some sort of recommended way to use CKEditor with Formik, but if not, I think this issue would be a good start to come up with one. Thanks!
I experience this exact same thing. Did you manage to solve it @rivernews?
@GuidovdRiet Unfortunately no new progress since then. But if I have time, I would try the "get-editor-data-only-when-save" route - maybe something you can try:
- Remove
onChangecallback function - Type in the editor, see if char shows up correctly. If so, we may assume
data={...}can be just the "initial editor content". - In Formik submit logic for this field, call
form.setFieldValue(field.name, editor.getData());so the form gets the latest editor data.- To get the
editorhandle, you can use<CKEditor onInit={(editor) => { this.editor = editor; }}, so later on you can callthis.editor.getData()in the submit logic.
- To get the
If above submits the right editor content, then it seems we can really forgo onChange on every keystroke and improve the performance.
Thank you for the quick response. I'm currently trying to figure out what is causing the problem. Thanks for thinking along.
Hi, I don't know what the difference is between the CKEditor and CKEditors, but thanks, thanks to your error, I reproduce that and its operation
< Field
name = "description"
validate = { validateText }
render = {({ field, form }) => {
return (
<>
<CKEditors
content={field.value}
events={{
"blur": () => { form.setFieldTouched(field.name, true) },
"afterPaste": afterPaste,
"change": (event) => { form.setFieldValue(field.name, event.editor.getData()) }
}}
/*
onChange={(event, editor) => {
form.setFieldValue(field.name, editor.getData());
}} */
/>
</>
)
}}
/>
wouldn't it be possible if we don't use CKEditor in a formik Field?! Why not exclude the ckeditor from formik and store it's data on a different state?. and then on formik submission, we read that state.
wouldn't it be possible if we don't use CKEditor in a formik Field?! Why not exclude the ckeditor from formik and store it's data on a different state?. and then on formik submission, we read that state.
hey, I don't know if it's going to help somebody but, I just used like that, and works fine.
const SendTextOrderForm = (props: ISendTextOrder) => {
const [, textMetaProps, textHelperProps] = useField(`text`);
return (
<CKEditor
editor={ClassicEditor}
name="text"
data={textMetaProps.value}
onChange={(event: any, editor: any) => {
const data = editor.getData();
textHelperProps.setValue(data);
}}
/>
);
};
The problem I have now is, that because I'm using TS and I have some @Type errors
npm i --save-dev @types/ckeditor__ckeditor5-react is missing from the npm repository
<Formik
initialValues={initialValues}
onSubmit={(values, { setSubmitting }) => {
createTrigger(values, setSubmitting);
}}
validationSchema={validationSchema}
>
{({
values,
errors,
handleChange,
handleSubmit,
isSubmitting,
setFieldValue,
}) => (
<form onSubmit={handleSubmit}>
<CKEditor
editor={FullEditor}
data={values.my_text_field}
onChange={ ( event, editor ) => {
const data = editor.getData();
setFieldValue('my_text_field', data);
} }
/>
</form>
)}
</Formik>