fix: debounce text block gRPC saves to eliminate typing lag (#2218)#2219
Conversation
…#2218) Root cause: Every keystroke in the text editor triggered a synchronous gRPC round-trip to middleware via setText() -> blockSetText() -> C.BlockTextSetText(). At normal typing speed (8-12 chars/sec), these piled up and blocked the main thread, causing visible freezes and buffered input. Additionally, a dependency-less useEffect called setValue() twice per render cycle — each call performing full HTML parsing, mark processing, innerHTML replacement, and mention/emoji rendering — doubling the already-expensive DOM work on every keystroke echo from middleware. Changes: - Debounce setText() in onKeyUpHandler with 300ms window. The contenteditable DOM already reflects typed text natively via the browser — only the gRPC persistence needs batching. - Remove duplicate setValue() call in the render-loop useEffect that was redundantly rebuilding the DOM a second time per render. - Flush debounce timer on critical paths (blur, keydown, paste, context menu open) to prevent stale overwrites and ensure no data loss. - Clean up debounce timer on component unmount. Fixes anyproto#2218
|
@ra3orblade Please look into this. |
needs testing thoroughly, we had the timeout logic before and it did not work good. for example when leaving the page it should save forcefully imediately in blur on unmount, and similar cases. |
Address maintainer review feedback: the unmount cleanup now force-saves any pending debounced text to middleware before the component is destroyed, preventing data loss when navigating away from a page while typing. Flush/save coverage: - onBlur: clears timer + immediate setText - onKeyDown: clears timer (structural keys do their own save) - onPaste: via onKeyDown path - context menu: clears timer + immediate setText - unmount: clears timer + force blockSetText if dirty
|
@ra3orblade Hey, totally fair point — that's exactly the kind of thing that can go wrong with timeouts. I actually just pushed a fix for the unmount case (b61958a). Now if the component unmounts while a save is pending, it force-flushes to middleware immediately before cleanup. The debounce only touches the onKeyUp typing path — blur, Enter, Backspace, paste, and page navigation all save synchronously like before. No timeout waiting on any of those. Would appreciate it if you could take a look at the updated code when you get a chance 🙏 |
|
@ra3orblade Hi, I hope you are doing well. I know you are super busy, but please when you get sometime just looked into this. Thanks a lot. |
|
The code itself looks fine, needs testing thoroughly anyway, so please double-check. |
Root cause: Every keystroke in the text editor triggered a synchronous gRPC round-trip to middleware via setText() -> blockSetText() -> C.BlockTextSetText(). At normal typing speed (8-12 chars/sec), these piled up and blocked the main thread, causing visible freezes and buffered input.
Additionally, a dependency-less useEffect called setValue() twice per render cycle — each call performing full HTML parsing, mark processing, innerHTML replacement, and mention/emoji rendering — doubling the already-expensive DOM work on every keystroke echo from middleware.
Changes:
Fixes #2218
Description
What type of PR is this? (check all applicable)
Related Tickets & Documents
Mobile & Desktop Screenshots/Recordings
Added tests?
Added to documentation?