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

perf: reduce re-rendering #353

Merged
merged 2 commits into from
Nov 18, 2024

Conversation

cycleccc
Copy link
Owner

@cycleccc cycleccc commented Nov 17, 2024

Changes Overview

Implementation Approach

Testing Done

Verification Steps

Additional Notes

Checklist

  • I have created a changeset for this PR if necessary.
  • My changes do not break the library.
  • I have added tests where applicable.
  • I have followed the project guidelines.
  • I have fixed any lint issues.

Related Issues

Summary by CodeRabbit

  • New Features

    • Enhanced textarea rendering functionality for improved user experience.
    • Introduced a new feature to compute differences based on the current selection in the editor.
    • Added a weak map to track selections associated with text areas.
    • Optimized rendering process to reduce unnecessary re-rendering within editor components.
  • Bug Fixes

    • Improved error handling and consistency in rendering updates.
  • Refactor

    • Simplified property assignments and improved function clarity for better maintainability.

@cycleccc cycleccc linked an issue Nov 17, 2024 that may be closed by this pull request
Copy link

changeset-bot bot commented Nov 17, 2024

⚠️ No Changeset found

Latest commit: 5262406

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

coderabbitai bot commented Nov 17, 2024

Caution

Review failed

The pull request is closed.

Walkthrough

The changes in this pull request focus on enhancing the functionality of the textarea view rendering in the update-view.ts file. A new function, diffBySelection, has been introduced to compute differences in virtual node children based on the current selection. Additionally, modifications to existing functions streamline the handling of the contentEditable property and improve clarity in parameter naming. The updateView function has been updated to utilize the new diffBySelection function for more efficient virtual node updates, alongside refined error handling for consistency.

Changes

File Path Change Summary
packages/core/src/text-area/update-view.ts - Added diffBySelection function for computing differences based on selection.
- Updated genRootVnode for simplified contentEditable assignment.
- Altered genRootElem function signature for clarity.
- Modified updateView to incorporate diffBySelection for efficient virtual node updates.
- Refined error handling with consistent null checks.
packages/core/src/utils/weak-maps.ts - Added new weak map TEXTAREA_TO_SELECTION to map TextArea instances to their Range objects.
- Reorganized import statements for clarity.

Possibly related PRs

  • perf: reduce re-rendering #353: This PR directly modifies the update-view.ts file, which is also the main PR's focus, enhancing the textarea view rendering functionality and introducing the diffBySelection function.

Poem

In the meadow where text flows bright,
A new function hops, bringing delight.
With selections now clear,
And updates so near,
The textarea dances, a joyful sight! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (1)
packages/core/src/text-area/update-view.ts (1)

Line range hint 47-54: Remove unused parameter _readOnly from genRootElem.

The _readOnly parameter in the genRootElem function is not used within the function body. Removing it can improve code clarity and avoid confusion.

Apply this diff to remove the unused parameter:

-function genRootElem(elemId: string, _readOnly = false): Dom7Array {
+function genRootElem(elemId: string): Dom7Array {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d4b184a and 1d11ac0.

📒 Files selected for processing (1)
  • packages/core/src/text-area/update-view.ts (8 hunks)
🔇 Additional comments (1)
packages/core/src/text-area/update-view.ts (1)

66-108: Efficient implementation of diffBySelection.

The diffBySelection function effectively optimizes rendering by updating only the necessary parts of the virtual DOM based on the current selection, reducing unnecessary re-renders. The logic appears sound and should enhance performance.

packages/core/src/text-area/update-view.ts Outdated Show resolved Hide resolved
@cycleccc cycleccc force-pushed the 309-编辑器内容过多输入数据容易卡顿 branch from 1d11ac0 to 5262406 Compare November 18, 2024 02:29
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (4)
packages/core/src/text-area/update-view.ts (4)

48-48: Consider removing the unused parameter.

The _readOnly parameter is not used in the function implementation. Consider removing it to improve code clarity.

-function genRootElem(elemId: string, _readOnly = false): Dom7Array {
+function genRootElem(elemId: string): Dom7Array {

104-120: Consider improving null checks and extracting selection logic.

The selection-based diffing optimization is good, but the code could be more maintainable.

Consider these improvements:

  1. Use optional chaining for null checks
  2. Extract selection caching logic to a separate function
-  if (
-    prevVnode
-    && cacheSelection
-    && Range.isCollapsed(cacheSelection)
-  ) {
+  if (prevVnode?.children && cacheSelection && Range.isCollapsed(cacheSelection)) {
     newVnode.children = diffBySelection(prevVnode, content, editor)
   } else {
     newVnode.children = content.map((node, i) => {
+  function shouldUseSelectionDiff(
+    prevVnode: VNode | undefined,
+    cacheSelection: Range | undefined
+  ): boolean {
+    return Boolean(
+      prevVnode?.children &&
+      cacheSelection &&
+      Range.isCollapsed(cacheSelection)
+    )
+  }
+
   // In updateView function
-  if (
-    prevVnode
-    && cacheSelection
-    && Range.isCollapsed(cacheSelection)
-  ) {
+  if (shouldUseSelectionDiff(prevVnode, cacheSelection)) {

147-147: Improve error handling consistency.

The null checks could be more consistent and use optional chaining.

-  if (curVnode == null || patchFn == null) { return }
+  if (!curVnode || !patchFn) return
   
   // ... later in the code ...
   
-  if (textareaElem == null) { return }
+  if (!textareaElem) return

Also applies to: 157-157


179-180: Simplify window assignment.

The window assignment could be simplified using optional chaining.

-    const window = getDefaultView(textareaElem)
-
-    // eslint-disable-next-line no-unused-expressions
-    window && EDITOR_TO_WINDOW.set(editor, window)
+    getDefaultView(textareaElem)?.pipe(window => 
+      EDITOR_TO_WINDOW.set(editor, window)
+    )
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1d11ac0 and 5262406.

📒 Files selected for processing (2)
  • packages/core/src/text-area/update-view.ts (8 hunks)
  • packages/core/src/utils/weak-maps.ts (2 hunks)
🔇 Additional comments (4)
packages/core/src/utils/weak-maps.ts (2)

7-9: LGTM! Import organization looks good.

The consolidation and reordering of imports improve code organization without affecting functionality.

Also applies to: 12-12, 15-15, 18-20


80-80: Verify the re-rendering optimization impact.

The new TEXTAREA_TO_SELECTION WeakMap is a good addition for tracking textarea selections efficiently. This aligns well with the PR's objective of reducing re-rendering by allowing precise tracking of selection changes.

Let's verify the optimization's implementation:

✅ Verification successful

Let me gather more context about the selection handling and re-rendering logic.


WeakMap optimization effectively reduces re-rendering scope

The TEXTAREA_TO_SELECTION WeakMap is correctly implemented as part of an optimization strategy that reduces unnecessary re-renders:

  • It caches the previous selection state and uses Range.isCollapsed check to determine if only a small portion needs updating
  • When the selection is collapsed (cursor position), it triggers diffBySelection which performs targeted updates instead of re-rendering all content
  • The WeakMap properly maintains the selection state while allowing garbage collection of unused TextArea references
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how the new WeakMap is utilized for optimization

# Look for usage of TEXTAREA_TO_SELECTION in the codebase
rg "TEXTAREA_TO_SELECTION" -A 5

# Check for related selection handling and re-rendering logic
ast-grep --pattern 'function diffBySelection($$$) {
  $$$
}'

Length of output: 1860


Script:

#!/bin/bash
# Check the update-view.ts file content to understand the selection handling logic
rg -B 5 -A 10 "cacheSelection" packages/core/src/text-area/update-view.ts

# Look for any selection-related performance optimizations
rg -B 3 -A 3 "Range.isCollapsed|selection" packages/core/src/text-area/update-view.ts

Length of output: 1507

packages/core/src/text-area/update-view.ts (2)

37-37: LGTM! Simplified contentEditable assignment.

The change to !readOnly improves code clarity while maintaining the same functionality.


65-89: 🛠️ Refactor suggestion

Consider handling multi-line selections and array bounds.

The current implementation has two potential issues:

  1. It only updates the node at startIndex, which might miss updates in multi-line selections where focus.path[0] differs from anchor.path[0].
  2. There's no validation that startIndex is within the bounds of the content array.

Let's verify if there are any existing tests covering these edge cases:

Consider updating the implementation to handle these cases:

 function diffBySelection(
   prevVnode: VNode,
   content: Descendant[],
   editor: IDomEditor,
 ): VNode[] {
   const selection = editor.selection

   if (!selection) {
     return prevVnode.children as VNode[]
   }

   const { anchor, focus } = selection

-  // 确定更新范围
-  const startIndex = Math.min(anchor.path[0], focus.path[0])
+  // Validate and get update range
+  const startIndex = Math.min(anchor.path[0], focus.path[0])
+  const endIndex = Math.max(anchor.path[0], focus.path[0])
+  
+  if (startIndex < 0 || startIndex >= content.length) {
+    return prevVnode.children as VNode[]
+  }

   // 克隆数组并更新指定位置
   const newChildren = [...(prevVnode.children || [])] as VNode[]
-  const newNode = node2Vnode(content[startIndex], startIndex, editor, editor)
-
-  normalizeVnodeData(newNode)
-  newChildren[startIndex] = newNode
+  
+  // Update all nodes in selection range
+  for (let i = startIndex; i <= endIndex && i < content.length; i++) {
+    const newNode = node2Vnode(content[i], i, editor, editor)
+    normalizeVnodeData(newNode)
+    newChildren[i] = newNode
+  }

   return newChildren
 }

@cycleccc cycleccc merged commit 9fb698d into master Nov 18, 2024
2 checks passed
cycleccc added a commit that referenced this pull request Nov 18, 2024
@coderabbitai coderabbitai bot mentioned this pull request Nov 23, 2024
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

编辑器内容过多表格数据容易卡顿
1 participant