Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"[Violation] 'xxx' handler took <N>ms" appeared in the Console, and input lag began to occur. #14188

Open
gfu7 opened this issue May 18, 2023 · 15 comments
Labels
domain:performance This issue reports a problem or potential improvement regarding editor performance. type:bug This issue reports a buggy (incorrect) behavior.

Comments

@gfu7
Copy link

gfu7 commented May 18, 2023

Background

I need to develop a rich text React Component and publish it to a private npm repository. After comparison, I ultimately chose "CKEditor" and developed the component according to the following tutorial, React rich text editor component.

Then import the component for use or pack and release as a library. When there is continuous copying and pasting with multiple content types, it can cause lagging issues.

📝 Provide detailed reproduction steps (if any)

  1. Create a project using the create-react-app scaffold.
  2. Integrating CKEditor5 according to this tutorial React rich text editor component / create-react-app.
  3. Modify the entry and output of webpack to package it into a simple library.
// webpack.config.js
...
    entry: ''../src/components/Editor/index.tsx",
     ...(isEnvProduction && {
      externals: {
        react: {
          root: 'React',
          commonjs2: 'react',
          commonjs: 'react',
          amd: 'react',
        },
        'react-dom': {
          root: 'ReactDOM',
          commonjs2: 'react-dom',
          commonjs: 'react-dom',
          amd: 'react-dom',
        },
      },
    }),
    output: {
         path: paths.appBuild,
         filename: 'index.js',
        ...(isEnvProduction && {
          library: {
            name: 'bba-rc-editor',
            type: 'umd',
          },
        }),
    }
...
  1. npm linkor publish a library install it.
  2. When there is continuous copying and pasting with multiple content types, it can cause lagging issues.

✔️ Expected result

Input content smoothly.

❌ Actual result

If only text is input, there will be no lag. However, if pictures or tables are inserted, and the content becomes more diverse, there is a higher likelihood of lag occurring.The more content there is, the more severe the lag.

❓ Possible solution

I have no idea how to solve this problem, Therefore I came there for help.
I used to think it was because there were too many plugins, but even after reducing some, the problem still persists.

📃 Other details

  • Browser: Chrome 113(Maybe it's not related to the version, as testers and operators' computer browsers also had this issue.)
  • OS: MacOS 12.6 /Windows
  • First affected CKEditor version: 37.1.0 (Because I wanted to use Typescript, I did not prioritize trying to lower the version to troubleshoot this issue.)
  • Installed CKEditor plugins:

import type { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
import { Essentials } from '@ckeditor/ckeditor5-essentials';
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
import { Heading } from '@ckeditor/ckeditor5-heading';
import {
  Bold,
  Italic,
  Underline,
  Subscript,
  Superscript,
  Code,
} from '@ckeditor/ckeditor5-basic-styles';
import {
  Table,
  TableToolbar
} from '@ckeditor/ckeditor5-table';
import { WordCount } from '@ckeditor/ckeditor5-word-count';
import { RemoveFormat } from '@ckeditor/ckeditor5-remove-format';
import { Font } from '@ckeditor/ckeditor5-font';
import { List, ListProperties } from '@ckeditor/ckeditor5-list';
import {
  Indent
} from '@ckeditor/ckeditor5-indent';
import { Highlight } from '@ckeditor/ckeditor5-highlight';
import { Alignment } from '@ckeditor/ckeditor5-alignment';
import { HorizontalLine } from '@ckeditor/ckeditor5-horizontal-line';
import {
  Link
} from '@ckeditor/ckeditor5-link';
import { BlockQuote } from '@ckeditor/ckeditor5-block-quote';
import {
  SpecialCharacters,
  SpecialCharactersEssentials,
} from '@ckeditor/ckeditor5-special-characters';
import { PageBreak } from '@ckeditor/ckeditor5-page-break';
import {
  Image,
  ImageToolbar,
  ImageCaption,
  ImageStyle,
  ImageResize,
  ImageInsert,
} from '@ckeditor/ckeditor5-image';
import { SourceEditing } from '@ckeditor/ckeditor5-source-editing';
import { Mention } from '@ckeditor/ckeditor5-mention';
import { TextPartLanguage } from '@ckeditor/ckeditor5-language';
import { PasteFromOffice } from '@ckeditor/ckeditor5-paste-from-office';
import { GeneralHtmlSupport } from '@ckeditor/ckeditor5-html-support';
import { Autoformat } from '@ckeditor/ckeditor5-autoformat';
//  custom plugins
import InsertStock from './plugins/insertStock/insertStock';
import FullScreen from './plugins/fullScreen/fullScreen';

Editor Component main code

import React from 'react';
import type { ReactElement, RefObject, RefCallback } from 'react';
import clsx from 'classnames';
import type { EventInfo } from '@ckeditor/ckeditor5-utils';
import type { EditorConfig } from '@ckeditor/ckeditor5-core';
import { merge } from 'lodash-es';
import omit from 'rc-util/lib/omit';
import { CKEditor } from '@ckeditor/ckeditor5-react';
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
import { setPresetPlugins, setPresetToolbar, presetHandlerOnReady } from './preset';
import { getPresetPluginConfig, getPluginConfigs } from './config';
import { fixControlledValue, resolveLanguage, resolveOnChange } from './utils';
import type { EditorProps, EditorRef } from './interface';

export type EditorInstance = ClassicEditor & {};
export type Ref<T> = RefCallback<T> | RefObject<T> | null;

/**
 * To be resolved: There is a lagging issue when inserting tables or pasting continuously, which results in the following warning prompt in the browser console. ⚠️
 * [Violation] 'click' handler took 235ms
 * [Violation] Forced reflow while executing JavaScript took 42ms
 * [Violation] 'setTimeout' handler took 55ms
 */

const Editor = (props: EditorProps): ReactElement => {
  const {
    prefixCls = 'bba-rc-editor',
    defaultValue,
    config,
    beforeConfig, // To modify the configuration content again before the configuration takes effect.
    className,
    style,
    onReady,
    onError,
    onChange,
    onFocus,
    onBlur,
    disabled = false,
    children,
  } = props;
  let value = props.value || defaultValue;
 // Temporary simulation of useRef.
  const editorRef: {
    current?: any;
  } = { current: undefined };

  // focus
  const focus = () => {
    if (editorRef?.current) {
      editorRef.current?.focus();
    }
  };

  // reset
  const reset = (event: React.MouseEvent<HTMLElement, MouseEvent>) => {
    value = '';
    focus();
    if (editorRef?.current?.sourceElement) {
      resolveOnChange(editorRef.current.sourceElement as HTMLTextAreaElement, event, onChange);
    }
  };

  const _onChange = (event: EventInfo, editor: ClassicEditor) => {
    const _value = editor.getData() || '';
    if (props.value === undefined) {
      // setValue(_value);
    }
    if (editor.sourceElement) {
      // Forging an HTMLTextAreaElement target object makes it easier to create input form components.
      resolveOnChange(editor.sourceElement as HTMLTextAreaElement, event, onChange, _value);
    }
  };

  // Set some behaviors as presets.
  const _onReady = (editor: ClassicEditor) => {
    onReady?.(editor);
    presetHandlerOnReady(editor);
  };

  const presetConfig: EditorConfig = {
    ...getPresetPluginConfig(),
  };

  const configMerged = merge(presetConfig, config);
  if (configMerged.language) {
    configMerged.language = resolveLanguage(configMerged.language);
  }
  setPresetPlugins(configMerged);
  setPresetToolbar(configMerged);
  let _config = beforeConfig ? beforeConfig(configMerged) : configMerged;
  _config = getPluginConfigs(_config);

  const otherProps = omit(props as EditorProps, [
    'prefixCls',
    'value',
    'data',
    'defaultValue',
    'beforeConfig',
    'className',
    'style',
    'onReady',
    'onError',
    'onChange',
    'onFocus',
    'onBlur',
    'disabled',
    'config',
  ]);

  return (
    <div
      className={clsx(
        prefixCls,
        {
          [`${prefixCls}-disabled`]: disabled,
        },
        className
      )}
      style={style}
    >
      <CKEditor
        disabled={disabled}
        editor={ClassicEditor}
        config={_config}
        data={fixControlledValue(value)}
        onReady={editor => {
          _onReady(editor);
          onReady?.(editor);
        }}
        onChange={(event, editor) => {
          _onChange?.(event, editor);
        }}
        onBlur={(event, editor) => {
          onBlur?.(event, editor);
        }}
        onFocus={(event, editor) => {
          onFocus?.(event, editor);
        }}
        onError={(error, details) => {
          onError?.(error, details);
        }}
        {...otherProps}
      />
      {children}
    </div>
  );
};

export default Editor;

When there is input lag, the message that appears on the console.

[Violation] 'click' handler took 228ms
[Violation] Forced reflow while executing JavaScript took 63ms
[Violation] 'setTimpout' handler took <N>ms
[Violation] 'paste' handler took <N>ms
[Violation] "beforeinout" handler took <N>ms
[Violation] 'paste' handler took <N>ms
[Violation] 'paste' handler took 180ms
[Violation] 'setTimeout' handler took 75ms

image

Note that: [Violation] 'xxx' handler took xxx ms from http://localhost:3000/Users/xxx/bba-rc-editor/node_modules/@ckeditor/ckeditor5-utils/src/dom/emittermixin.js

thanks to read.

If you'd like to see this fixed sooner, add a 👍 reaction to this post.

@gfu7 gfu7 added the type:bug This issue reports a buggy (incorrect) behavior. label May 18, 2023
@gfu7
Copy link
Author

gfu7 commented May 22, 2023

I am continuing to troubleshoot this issue:
First, clone the ckeditor5-react-example (Version: 35.4.0)repository to my local machine.
Next, run "npm install && npm run start" to start the project, without making any modifications.
Finally, copy and paste the extremely long text into the editor input field.

Something bad has happened. When I tried to continue typing, I feel obvious input lag. By adding the WordCount plugin, it displays the current Characters: 212217, Words: 28012.

Are the characters and words too long? No, I tried to copy the text in the below editor and paste it into the input box of the official example full-featured-editor, with the same characters and words. However, I can continue to type quickly and do not feel any input delay in full-featured-editor, while in the editor of ckeditor5-react-example, I cannot continue to input text fluently.

so, whose issue is it? "@ckeditor/ckeditor5-react"、create-react-app。

@Witoso
Copy link
Member

Witoso commented May 22, 2023

Hey, thanks for reaching out. Could you check how your text behaves with the latest editor versions? We are at v38 right now.
It could be done with the demos in the https://github.com/ckeditor/ckeditor5-react repo.

@Mgsy we should refresh or archive the https://github.com/ckeditor/ckeditor5-react-example, WDYT?

@gfu7
Copy link
Author

gfu7 commented May 23, 2023

@Witoso Thank you for your reply, I have checked a long text typing behaves with the latest editor versions as per your instructions. The result is the occurrence of input lag in version v18 too. I public the example code repository https://github.com/gfu7/ckeditor5-react-example.

As of now, the following test results are listed:

  1. @ckeditor/[email protected] + @ckeditor/[email protected] + [email protected] --- ✕
  2. @ckeditor/[email protected] + @ckeditor/[email protected] + create-react-app@2 (Offical React Example) --- ✕
  3. @ckeditor/[email protected] + @ckeditor/[email protected] + [email protected] --- ✕
  4. @ckeditor/[email protected] + @ckeditor/[email protected] + [email protected] (Latest Version Editor Example) --- ✕
  5. Official Example Full-featured editor --- ✓

I will try to downgrade the editor gradually. I used v26 a few years ago and it doesn't seem to have the issue of delayed input after long text input.

@gfu7
Copy link
Author

gfu7 commented May 23, 2023

Following the tutorial https://ckeditor.com/docs/ckeditor5/latest/installation/integrations/vuejs-v3.html, I created a Vue(v3) application example and found that there is a input delay issue in the testing results. When I input a long text and want to continue typing, there is a noticeable delay.

Here is a link to my test code repository https://github.com/gfu7/ckeditor5-vue-example
@ckeditor/[email protected] + [email protected]

@Mgsy
Copy link
Member

Mgsy commented May 23, 2023

@Mgsy we should refresh or archive the https://github.com/ckeditor/ckeditor5-react-example, WDYT?

You are right, we should bump versions in this repository. We will handle it. I'm not sure if we should archive it, as it is still a good example of the integration, but we should definately make sure it uses updated dependencies 😄

@Witoso
Copy link
Member

Witoso commented May 23, 2023

@gfu7 could you send us the data you're adding to the editor? ist it plain text, markup? What content are you pasting?

@Witoso Witoso added the domain:performance This issue reports a problem or potential improvement regarding editor performance. label May 23, 2023
@gfu7
Copy link
Author

gfu7 commented May 24, 2023

@Witoso I am glad to see your reply,Here are my testing steps.

  1. copy the default content (The initial value has Words: 358 Characters: 2227 in the full-featured example editor https://ckeditor.com/docs/ckeditor5/38.0.0/examples/builds-custom/full-featured-editor.html .
  2. paste the copied content into the current full-featured example editor.
  3. repeat the above operation 5 times. (At this time, the editor has about Words: 11456 Characters: 71264)
  4. copy content that in the full-featured example editor, and paste it into the test editor https://github.com/ckeditor/ckeditor5-react-example .
  5. in the testing editor, try to continue typing, such as hold down the 1 number key on the keyboard,I can feel there is a lag in the input.
  6. return to full-featured example editor, make the editor focus, hold down the 1 number key on the keyboard, I didn't feel any lag in input.

@gfu7
Copy link
Author

gfu7 commented May 24, 2023

There's been some new progress. Maybe it's caused by editor.getData().

<CKEditor
  editor={ClassicEditor}
  config={editorConfiguration}
  data={testLongData}
  onReady={(editor) => {
    // You can store the "editor" and use when it is needed.
    console.log("Editor is ready to use!", editor);
  }}
  onChange={(event, editor) => {
    console.time("editor.getData()");
    const data = editor.getData();
    console.timeEnd("editor.getData()");
    console.log({ event, editor, data });
  }}
  onBlur={(event, editor) => {
    console.log("Blur.", editor);
  }}
  onFocus={(event, editor) => {
    console.log("Focus.", editor);
  }}
/>

By adding console.time to print the time, it was found that the execution of getData() takes 200~300ms in the scenario of plain text Words: 88514 Characters: 472353.

editor.getData(): 278.56201171875 ms
editor.getData(): 219.701904296875 ms
editor.getData(): 221.0888671875 ms

Perhaps 90000 words is not a common real-life scenario, but increasing the number of plugins will increase the execution time of editor.getData(). Frequent calls and blocking of the main thread can lead to lag, so can we no longer directly call editor.getData() in the onChange event listener? At least add an appropriate throttling function.

The following is debug code:
gfu7/ckeditor5-react-example@481a8d2

@Witoso
Copy link
Member

Witoso commented May 24, 2023

Yes, the frequent calling of editor.getData is very costly, especially for large content. Try to use the autosave plugin to throttle and save only in an interval when typing inserting content finishes.

@gfu7
Copy link
Author

gfu7 commented May 25, 2023

@Witoso Thanks, getData is better in the autosave plugin. Not executing editor.getData() in the onChange event obviously reduces input lag. As the length of content and the number of imported plugins increase, the cost of editor.getData() also increases. When the execution time of editor.getData() takes more than 100ms, users will feel lag and delay.

Suggested modification of the sample code https://ckeditor.com/docs/ckeditor5/latest/installation/integrations/react.html : do not call editor.getData() in the onChange event. To avoid misleading developers.(Or provide an API that allows timely access to input box data without relying on an automatic save plugin.)

Currently, after entering long content, the input fluency still cannot compare to the full-featured example editor https://ckeditor.com/docs/ckeditor5/38.0.0/examples/builds-custom/full-featured-editor.html. and some warning are still appearing in the console, from: /node_modules/@ckeditor/ckeditor5-utils/src/dom/emittermixin.js

[Violation] 'click' handler took 228ms
[Violation] Forced reflow while executing JavaScript took 63ms
[Violation] 'setTimpout' handler took <N>ms
[Violation] 'paste' handler took <N>ms
[Violation] "beforeinout" handler took <N>ms
[Violation] 'paste' handler took <N>ms
[Violation] 'paste' handler took 180ms
[Violation] 'setTimeout' handler took 75ms

I have no idea how to resolve these warnings.

@gfu7
Copy link
Author

gfu7 commented May 26, 2023

Why do I tend to get the latest content more frequently?

In practical development, rich text editor component can be understood as a TextArea Element and are one of the items in a form. In order to better use the form, it is necessary to transform the editor component into a controlled component.

Under Control Model

After wrapped by Form.Item with name property, value(or other property defined by valuePropName) onChange(or other property defined by trigger) props will be added to form controls, the flow of form data will be handled by Form which will cause:

  1. You shouldn't use onChange on each form control to collect data(use onValuesChange of Form), but you can still listen to onChange.
  2. You cannot set value for each form control via value or defaultValue prop, you should set default value with initialValues of Form. Note that initialValues cannot be updated by setState dynamically, you should use setFieldsValue in that situation.
  3. You shouldn't call setState manually, please use form.setFieldsValue to change value programmatically.

Reference from: https://ant.design/components/form#formitem

so, as a controlled component,The following data update flow.

  1. Operation personnel edit content.
  2. Use editor.getData() let the editor try to retrieve the latest data.
  3. Call onChange(React.ChangeEventHandler) to update form value programmatically.

Frequent use of editor.getData() is to timely update the values of the form, ensuring that the latest input content of the editor is submitted when the form is submitted.

<ProForm.Item
    name={'title'}
    label={'Article Title'}
  >
  <Input placeholder="Please enter the title." />
</ProForm.Item>
<ProForm.Item
    name={'content'}
    label={'Article Content'}
  >
  <Editor />
</ProForm.Item>

@Witoso
Copy link
Member

Witoso commented May 26, 2023

Frequent use of editor.getData() is to timely update the values of the form, ensuring that the latest input content of the editor is submitted when the form is submitted.

Why don't you get data on the form submission (only once)?

@gfu7
Copy link
Author

gfu7 commented May 27, 2023

@Witoso To improve the development experience of the team. I provide the team with common components and hope they can use the editor component just like the input component. In the form, the item entered in the editor is just one of the items. I don't want all team members to have to getData() extra when using the editor component as part of the form. They can simply use form.getFieldsValue() to get the input values of all form fields.

@Witoso
Copy link
Member

Witoso commented May 29, 2023

Gotcha, thanks for the info!

@ravinaginoya
Copy link

ravinaginoya commented Aug 2, 2024

getting this error while type something in input field , and not able to see type value instatntly , it will take time to show ,[Violation] 'setTimeout' handler took ms
[Violation] 'message' handler took ms
getting above error while i refresh page i get The key "target-densitydpi" is not supported.
Any can help to resolve this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
domain:performance This issue reports a problem or potential improvement regarding editor performance. type:bug This issue reports a buggy (incorrect) behavior.
Projects
None yet
Development

No branches or pull requests

4 participants