ckeditor / ckeditor5-react

Official CKEditor 5 React component.
https://ckeditor.com/ckeditor-5
Other
416 stars 100 forks source link

Correct Usage working with Formik for CKEditor #205

Open rivernews opened 3 years ago

rivernews commented 3 years ago

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!

GuidovdRiet commented 2 years ago

I experience this exact same thing. Did you manage to solve it @rivernews?

rivernews commented 2 years ago

@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:

  1. Remove onChange callback function
  2. Type in the editor, see if char shows up correctly. If so, we may assume data={...} can be just the "initial editor content".
  3. 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 editor handle, you can use <CKEditor onInit={(editor) => { this.editor = editor; }}, so later on you can call this.editor.getData() in the submit logic.

If above submits the right editor content, then it seems we can really forgo onChange on every keystroke and improve the performance.

GuidovdRiet commented 2 years ago

Thank you for the quick response. I'm currently trying to figure out what is causing the problem. Thanks for thinking along.

preppygassou commented 2 years ago

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()); }}
/ /> </> ) }} />

osamana commented 1 year ago

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.

osamana commented 1 year ago

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.

davidenascimento91 commented 1 year ago

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

SubhanSh commented 9 months ago
<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>