From b3351707e29b18a5b836fb8013e8d00eb0301df5 Mon Sep 17 00:00:00 2001 From: MartinBraquet Date: Thu, 23 Jul 2026 18:14:30 +0200 Subject: [PATCH] Optimize editor performance by reducing excessive re-renders - Introduced `useEditorState` hook for targeted subscriptions in components (e.g., text length, active marks, editor state). - Disabled global re-rendering on every transaction within the editor. - Improved typing responsiveness across components by limiting reflows and re-renders caused by keystrokes. - Updated components (`LLMExtractSection`, `comment-input`, `editable-bio`, etc.) to leverage `useEditorState` for localized updates. - Refined floating toolbar behavior with `requestAnimationFrame` for smoother scrolling and reduced layout thrashing. --- web/components/bio/editable-bio.tsx | 8 ++++- web/components/comments/comment-input.tsx | 14 ++++++-- web/components/contact.tsx | 7 +++- .../editor/floating-format-menu.tsx | 32 ++++++++++++++----- .../filters/incomplete-profiles-toggle.tsx | 32 +++++++++++++++---- web/components/llm-extract-section.tsx | 15 ++++++--- .../messaging/send-message-button.tsx | 9 +++++- web/components/widgets/editor.tsx | 25 ++++++++++++++- 8 files changed, 115 insertions(+), 27 deletions(-) diff --git a/web/components/bio/editable-bio.tsx b/web/components/bio/editable-bio.tsx index a145e584..2e83e97b 100644 --- a/web/components/bio/editable-bio.tsx +++ b/web/components/bio/editable-bio.tsx @@ -1,4 +1,5 @@ import {Editor} from '@tiptap/core' +import {useEditorState} from '@tiptap/react' import {MIN_BIO_LENGTH} from 'common/constants' import {MAX_DESCRIPTION_LENGTH} from 'common/envs/constants' import {Profile, ProfileWithoutUser} from 'common/profiles/profile' @@ -174,7 +175,12 @@ export function BaseBio({defaultValue, onBlur, onEditor, onClickTips}: BaseBioPr "Tell us all the details about yourself — and what you're looking for!", ), }) - const textLength = editor?.getText().length ?? 0 + // Subscribe to the text length; the editor no longer re-renders us per keystroke (see editor.tsx). + const textLength = + useEditorState({ + editor, + selector: ({editor}) => editor?.getText().length ?? 0, + }) ?? 0 const remainingChars = MIN_BIO_LENGTH - textLength useEffect(() => { diff --git a/web/components/comments/comment-input.tsx b/web/components/comments/comment-input.tsx index d855087c..807746c6 100644 --- a/web/components/comments/comment-input.tsx +++ b/web/components/comments/comment-input.tsx @@ -1,5 +1,5 @@ import {PaperAirplaneIcon} from '@heroicons/react/24/solid' -import {Editor} from '@tiptap/react' +import {Editor, useEditorState} from '@tiptap/react' import clsx from 'clsx' import {MAX_COMMENT_LENGTH, ReplyToUserInfo} from 'common/comment' import {User} from 'common/user' @@ -157,6 +157,14 @@ export function CommentInputTextArea(props: { } = props const t = useT() + // The editor no longer re-renders this component on every keystroke (see editor.tsx), so subscribe + // to just the emptiness of the doc — that's all the send buttons below need to enable/disable. + const isEmpty = + useEditorState({ + editor, + selector: ({editor}) => editor?.isEmpty ?? true, + }) ?? true + useEffect(() => { editor?.setEditable(!isSubmitting) }, [isSubmitting, editor]) @@ -211,7 +219,7 @@ export function CommentInputTextArea(props: { {user && !isSubmitting && submit && commentTypes.includes('repost') && (