Skip to content

perf: actually debounce rich text editor field value updates to only process latest state #12086

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

Conversation

philipp-tailor
Copy link
Contributor

Follow-up work to #12046, which was misnamed. It improved UI responsiveness of the rich text field on CPU-limited clients, but didn't actually reduce work by debouncing. It only improved scheduling.

Using requestIdleCallback lead to better scheduling of change event handling in the rich text editor, but on CPU-starved clients, this leads to a large backlog of unprocessed idle callbacks. Since idle callbacks are called by the browser in submission order, the latest callback will be processed last, potentially leading to large time delays between a user typing, and the form state having been updated. An example: When a user types "I", and the change events for the character "I" is scheduled to happen in the next browser idle time, but then the user goes on to type "love Payload", there will be 12 more callbacks scheduled. On a slow system it's preferable if the browser right away only processes the event that has the full editor state "I love Payload", instead of only processing that after 11 other idle callbacks.

So this code change keeps track when requesting an idle callback and cancels the previous one when a new change event with an updated editor state occurs.

Follow-up work to payloadcms#12046: Debounces rich text editor `setState()` calls.

Using `requestIdleCallback` leads to better scheduling of change event
handling in the rich text editor, but on CPU-starved clients, this leads
to a large backlog of unprocessed idle callbacks. Since idle callbacks
are called by the browser in submission order, the latest callback will
be processed last, potentially leading to large time delays between a
user typing, and the form state having been updated. An example:
When a user types "I", and the change events for the character "I" is
scheduled to happen in the next browser idle time, but then the user
goes on to type "love Payload", there will be 12 more callbacks
scheduled. On a slow system it's preferable if the browser right away
only processes the event that has the full editor state "I love Payload",
instead of only processing that after 11 other idle callbacks.

So this code change keeps track when requesting an idle callback and
cancels the previous one when a new change event with an updated editor
state occurrs.
@philipp-tailor philipp-tailor force-pushed the improve-rich-text-editor-reactivity-2 branch from 7f86b5b to c2472ac Compare April 14, 2025 13:32
@GermanJablo GermanJablo self-requested a review May 6, 2025 12:55
@GermanJablo GermanJablo self-assigned this May 6, 2025
Copy link
Contributor

@GermanJablo GermanJablo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The linter indicates that the "React Hook useCallback has a missing dependency: 'updateFieldValue'."

Previously, the eslint rule could be disabled on this line, since the only external dependency on updateFiledValue is setValue, which is already in the dependency array.

However, with the introduction of the React compiler, even if we know what we're doing, it's necessary not to violate the linter rules, since they are the same ones used by the React compiler, and a violation would mean the file would be skipped.

The solution here is either to add updateFieldValue to the dependency array or use our useEffectEvent hook, which mimics the experimental React hook with the same name.

You didn't need to know this, of course. Our codebase doesn't yet have 100% coverage of the React compiler, but that's the goal we're aiming for.

Inlines function into `useCallback`, which solves the linting issue that
the `useCallback` had a missing dependency `updateFieldValue`.

Addresses payloadcms#12086 (review).
@philipp-tailor
Copy link
Contributor Author

@GermanJablo Thank you for the pointer, addressed the dependencies of useCallback issue.

Copy link
Contributor

@GermanJablo GermanJablo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@GermanJablo GermanJablo merged commit 1d5d96d into payloadcms:main May 14, 2025
76 checks passed
kendelljoseph pushed a commit that referenced this pull request May 15, 2025
…process latest state (#12086)

Follow-up work to #12046, which was misnamed. It improved UI
responsiveness of the rich text field on CPU-limited clients, but didn't
actually reduce work by debouncing. It only improved scheduling.

Using `requestIdleCallback` lead to better scheduling of change event
handling in the rich text editor, but on CPU-starved clients, this leads
to a large backlog of unprocessed idle callbacks. Since idle callbacks
are called by the browser in submission order, the latest callback will
be processed last, potentially leading to large time delays between a
user typing, and the form state having been updated. An example: When a
user types "I", and the change events for the character "I" is scheduled
to happen in the next browser idle time, but then the user goes on to
type "love Payload", there will be 12 more callbacks scheduled. On a
slow system it's preferable if the browser right away only processes the
event that has the full editor state "I love Payload", instead of only
processing that after 11 other idle callbacks.

So this code change keeps track when requesting an idle callback and
cancels the previous one when a new change event with an updated editor
state occurs.
Copy link
Contributor

🚀 This is included in version v3.38.0

kendelljoseph pushed a commit that referenced this pull request May 19, 2025
…process latest state (#12086)

Follow-up work to #12046, which was misnamed. It improved UI
responsiveness of the rich text field on CPU-limited clients, but didn't
actually reduce work by debouncing. It only improved scheduling.

Using `requestIdleCallback` lead to better scheduling of change event
handling in the rich text editor, but on CPU-starved clients, this leads
to a large backlog of unprocessed idle callbacks. Since idle callbacks
are called by the browser in submission order, the latest callback will
be processed last, potentially leading to large time delays between a
user typing, and the form state having been updated. An example: When a
user types "I", and the change events for the character "I" is scheduled
to happen in the next browser idle time, but then the user goes on to
type "love Payload", there will be 12 more callbacks scheduled. On a
slow system it's preferable if the browser right away only processes the
event that has the full editor state "I love Payload", instead of only
processing that after 11 other idle callbacks.

So this code change keeps track when requesting an idle callback and
cancels the previous one when a new change event with an updated editor
state occurs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants