Skip to content

feat: fix multiple re-renders on user-input and split the file#3768

Merged
waleedlatif1 merged 2 commits intosimstudioai:stagingfrom
adithyaakrishna:feat/user-input
Mar 25, 2026
Merged

feat: fix multiple re-renders on user-input and split the file#3768
waleedlatif1 merged 2 commits intosimstudioai:stagingfrom
adithyaakrishna:feat/user-input

Conversation

@adithyaakrishna
Copy link
Contributor

@adithyaakrishna adithyaakrishna commented Mar 25, 2026

Summary

  • Fixes multiple re-renders on the input area and the arrow up icon.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Other: QOL

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Screenshots/Videos

After

Screen.Recording.2026-03-26.at.12.47.08.AM.mov

Before

Screen.Recording.2026-03-26.at.12.22.02.AM.mov

@cursor
Copy link

cursor bot commented Mar 25, 2026

PR Summary

Medium Risk
Moderate risk: refactors interactive chat input logic (attachments, mention insertion, voice input, submit) to reduce re-renders, which could introduce subtle regressions in cursor/focus or stale state handling despite being UI-scoped.

Overview
Refactors the home UserInput into a set of memoized client components (PlusMenuDropdown, SendButton, MicButton, AttachedFilesList, DropOverlay, AnimatedPlaceholderEffect) and centralizes shared constants/types in _components/constants.ts.

To reduce unnecessary re-renders and stale closures, UserInput now routes file/context/message operations through refs (filesRef, contextRef, valueRef), uses an imperative PlusMenuDropdown open() API for @ insertion, and computes the overlay highlight content via useMemo while syncing overlay scroll via a dedicated handler.

Written by Cursor Bugbot for commit 6b105a2. This will update automatically on new commits. Configure here.

@vercel
Copy link

vercel bot commented Mar 25, 2026

@adithyaakrishna is attempting to deploy a commit to the Sim Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 25, 2026

Greptile Summary

This PR splits the monolithic user-input.tsx into focused sub-components under a new _components/ barrel, and applies React.memo throughout to eliminate unnecessary re-renders — in particular the animated-placeholder animation, which previously caused the entire input to re-render on every tick.

Key changes:

  • AnimatedPlaceholderEffect — a null-rendering component that isolates the useAnimatedPlaceholder hook's state to its own subtree, imperatively updating textarea.placeholder via useEffect without triggering a parent re-render
  • AttachedFilesList, MicButton, SendButton, DropOverlay — all extracted and wrapped in React.memo; stable callbacks in the parent (via useCallback + filesRef) ensure the memoization is effective
  • PlusMenuDropdown — extracted as React.memo(React.forwardRef(...)) with internal filteredItemsRef to avoid stale closures in the keyboard-navigation handler
  • constants.ts — consolidates types, CSS class-name constants, and utility helpers (autoResizeTextarea, mapResourceToContext) per the dedicated config-file convention
  • index.ts barrel cleanly re-exports only the public surface; internal-only symbols (SEND_BUTTON_*, SpeechRecognitionStatic) are intentionally omitted

Confidence Score: 5/5

  • Safe to merge — the refactoring is functionally equivalent to the original monolith with correct memoization and no behavioral changes.
  • All extracted components faithfully replicate the original logic. React.memo wrapping is paired with stable props (useCallback + refs) in the parent so memoization is actually effective. The only open item is a non-blocking style suggestion to merge the two consecutive import blocks from the same barrel.
  • No files require special attention.

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/home/components/user-input/_components/animated-placeholder-effect.tsx New null-rendering component that isolates the animated placeholder's state updates into its own subtree, preventing the parent UserInput from re-rendering on each animation tick. Clean implementation.
apps/sim/app/workspace/[workspaceId]/home/components/user-input/_components/constants.ts Consolidated config file holding all types, class-name constants, and helper utilities for the user-input components. Follows the dedicated config file convention.
apps/sim/app/workspace/[workspaceId]/home/components/user-input/_components/index.ts Barrel export for the components folder. Exports all components, constants, and types cleanly. SpeechRecognitionStatic and SEND_BUTTON* constants are intentionally omitted (internal use only).
apps/sim/app/workspace/[workspaceId]/home/components/user-input/_components/plus-menu-dropdown.tsx Extracted PlusMenuDropdown as React.memo(React.forwardRef(...)). filteredItemsRef avoids stale closure in handleSearchKeyDown. All callbacks wrapped in useCallback. Solid implementation.
apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx Refactored to use extracted sub-components from _components barrel. Duplicate import blocks (import type + import) from the same barrel path could be merged. All drag/file/context handlers use useCallback with refs to stay stable across renders.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    UI["UserInput (no memo)"]

    UI -->|"renders (isolated subtree)"| APE["AnimatedPlaceholderEffect\n(React.memo)\n← isolates placeholder animation\nre-renders here only"]
    UI -->|"stable callbacks via filesRef"| AFL["AttachedFilesList\n(React.memo)"]
    UI -->|"isDragging flag"| DO["DropOverlay\n(React.memo)"]
    UI -->|"isListening + stable onToggle"| MB["MicButton\n(React.memo)"]
    UI -->|"stable props + forwardRef"| PMD["PlusMenuDropdown\n(React.memo + forwardRef)"]
    UI -->|"isSending + canSubmit"| SB["SendButton\n(React.memo)"]

    APE -->|"useEffect: sets textarea.placeholder\nwithout re-rendering parent"| TA["<textarea> DOM node"]

    subgraph "_components/index.ts (barrel)"
        APE
        AFL
        DO
        MB
        PMD
        SB
    end
Loading

Reviews (1): Last reviewed commit: "chore: split user-input" | Re-trigger Greptile

@vercel
Copy link

vercel bot commented Mar 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Mar 25, 2026 8:26pm

Request Review

@waleedlatif1 waleedlatif1 merged commit 54a862d into simstudioai:staging Mar 25, 2026
11 of 12 checks passed
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

if (textareaRef.current) {
textareaRef.current.placeholder = placeholder
}
}, [placeholder, textareaRef])
Copy link

Choose a reason for hiding this comment

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

Placeholder flickers due to useEffect instead of useLayoutEffect

Medium Severity

AnimatedPlaceholderEffect uses useEffect to imperatively set the textarea's placeholder, but the textarea renders with placeholder=''. Since useEffect fires after the browser paints, users see a brief flash of an empty placeholder on mount and whenever isInitialView toggles. Previously, placeholder was set directly as a JSX prop (synchronous, no flash). The parent component already uses useLayoutEffect for similar imperative DOM mutations on the same textarea, so switching to useLayoutEffect here would preserve the original behavior without the flicker.

Additional Locations (1)
Fix in Cursor Fix in Web

Sg312 pushed a commit that referenced this pull request Mar 25, 2026
…file (#3768)

* feat: fix rerenders

* chore: split user-input
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.

2 participants