Skip to content

fix(ui) reduce redundant rerenders#3593

Merged
TheodoreSpeaks merged 4 commits intostagingfrom
fix/render-resource
Mar 15, 2026
Merged

fix(ui) reduce redundant rerenders#3593
TheodoreSpeaks merged 4 commits intostagingfrom
fix/render-resource

Conversation

@TheodoreSpeaks
Copy link
Collaborator

Summary

Reduce redundant rerenders

  • mothership chat streaming shouldn't rerender the resource view
  • viewing existing resources shouldn't rerender the resource if its open

Type of Change

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

Testing

  • Validated streaming text doesn't rerender right side panel
  • Validated reading a file doesn't rerender any more
  • Validated modifying resources live updates

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

@vercel
Copy link

vercel bot commented Mar 15, 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 15, 2026 1:53am

Request Review

@cursor
Copy link

cursor bot commented Mar 15, 2026

PR Summary

Low Risk
Primarily memoization and small state-selection tweaks; low functional risk, though incorrect prop identity or registry selector behavior could cause missed UI updates in edge cases.

Overview
Reduces redundant rerenders in the Mothership resource panel. MothershipView and ResourceContent are wrapped in memo, and EmbeddedWorkflow’s useWorkflowRegistry subscriptions are narrowed to avoid re-rendering on unrelated hydration state changes.

Tightens resource event handling in chat streaming. useChat’s addResource now returns a boolean and SSE handlers use it to avoid duplicate adds, only trigger onResourceEvent when something was newly added, and still switch the active resource when an already-known resource is referenced (including workflow registry activation/load behavior adjustments).

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

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 15, 2026

Greptile Summary

This PR reduces redundant React re-renders in the Mothership chat view by wrapping MothershipView and ResourceContent in React.memo, consolidating Zustand selectors in EmbeddedWorkflow to compute derived booleans inside the selector (preventing subscriptions to intermediate fields), and using addResource's boolean return value to gate side-effect calls like onResourceEvent and ensureWorkflowInRegistry.

Key changes:

  • MothershipView and ResourceContent are now wrapped in React.memo, preventing re-renders during streaming since all callback props are stable (useCallback with no deps).
  • EmbeddedWorkflow now subscribes to two computed booleans (isMetadataLoaded, hasLoadError) via consolidated selectors rather than two raw state slices — the correct Zustand pattern for derived state.
  • In tool_result (read tool), onResourceEvent now only fires if the resource was newly added, avoiding panel re-expansion on every file read.
  • In resource_added, the event and active-resource selection are skipped when the resource already exists and is already active, preventing streaming-induced re-renders in the resource panel.

One potential behavior regression: When !wasAdded && activeResourceIdRef.current === resource.id, neither setActiveResourceId nor onResourceEvent is called. This means if a user manually collapses the resource panel while a resource is active, a subsequent resource_added event for that same resource will not re-expand the panel — a subtle departure from the previous behavior where the panel would always re-expand on resource_added.

Confidence Score: 3/5

  • Safe to merge with minor edge-case risk around panel expand behavior for already-active resources.
  • The memo optimizations and selector improvements are correct and risk-free. The main uncertainty is the change to when onResourceEvent fires — specifically when a resource already exists and is the active resource: the panel will no longer auto-expand for that case, which could silently break the UX if the user collapsed the panel manually. The workflow registry logic also skips ensureWorkflowInRegistry for existing resources, which could miss title updates in an edge case.
  • apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts — review the resource_added handler logic for the !wasAdded branch.

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts Core logic change: uses addResource's boolean return to gate onResourceEvent calls and prevent redundant panel-expand triggers; subtle edge case where an active-but-collapsed panel won't re-expand when the same resource arrives via resource_added.
apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx Wrapped in React.memo to prevent re-renders during chat streaming; all parent-passed callbacks are stable (useCallback), making memo effective.
apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx Wrapped ResourceContent in memo; EmbeddedWorkflow selectors consolidated to compute derived booleans inside the selector (correct Zustand pattern to reduce subscriptions).
apps/sim/app/workspace/[workspaceId]/home/home.tsx Only change is removal of an explanatory comment inside handleResourceEvent; no functional impact.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[SSE: resource_added] --> B[addResource - returns bool]
    B -->|wasAdded=true - new| C[invalidateResourceQueries]
    B -->|wasAdded=false - exists| C
    C --> D{wasAdded?}
    D -->|true| E[onResourceEvent - expand panel]
    D -->|false| F{activeResourceId == resource.id?}
    F -->|no| G[setActiveResourceId + onResourceEvent]
    F -->|yes| H[no-op - skip expand]
    E --> I{resource.type == workflow?}
    G --> I
    H --> I
    I -->|wasAdded && ensureWorkflowInRegistry| J[setActiveWorkflow]
    I -->|else - !wasAdded or registry fail| K[loadWorkflowState]

    style H fill:#f9c74f,stroke:#f3722c
    style J fill:#90be6d,stroke:#43aa8b
    style K fill:#577590,color:#fff,stroke:#277da1
Loading

Comments Outside Diff (1)

  1. apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts, line 608-617 (link)

    ensureWorkflowInRegistry skipped for existing workflow resources

    Previously, ensureWorkflowInRegistry was always called on resource_added for workflows. Now it is only called when wasAdded is true. When wasAdded is false, the code falls through to loadWorkflowState directly.

    ensureWorkflowInRegistry is responsible for creating/updating the registry entry (including the resource title). If the server sends a resource_added event for a workflow that was already in the resource list but with an updated title (e.g., the user renamed it), the registry entry's title will not be refreshed, and loadWorkflowState will be called instead. Depending on what loadWorkflowState does (whether it refreshes the title), this could leave a stale display name in the registry until a full reload.

    This is a minor concern and likely fine in practice, but worth double-checking that loadWorkflowState covers title updates for existing entries.

Last reviewed commit: 5e95d33

@TheodoreSpeaks
Copy link
Collaborator Author

@greptile review

Comment on lines +599 to +606
if (!wasAdded) {
if (activeResourceIdRef.current !== resource.id) {
setActiveResourceId(resource.id)
onResourceEventRef.current?.()
}
} else {
onResourceEventRef.current?.()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Panel won't re-expand for active-but-collapsed resource

When !wasAdded and activeResourceIdRef.current === resource.id, neither setActiveResourceId nor onResourceEventRef.current?.() is called. This means if the user manually collapsed the resource panel while a resource was active, a subsequent resource_added event for that same resource will not re-expand the panel, even though the PR's goal is for the panel to be visible when the AI references that resource.

Before this PR, onResourceEventRef.current?.() was always fired on resource_added, so handleResourceEvent would re-expand the panel whenever it was collapsed. The new logic silently skips the expand when the resource is already "active" by ID, regardless of panel collapse state.

If the intent is only to avoid the rerender (not the expand), consider still firing the event when the panel is collapsed:

if (!wasAdded) {
  if (activeResourceIdRef.current !== resource.id) {
    setActiveResourceId(resource.id)
    onResourceEventRef.current?.()
  } else if (isResourceCollapsedRef?.current) {
    // Resource is active but panel was manually collapsed — re-expand
    onResourceEventRef.current?.()
  }
} else {
  onResourceEventRef.current?.()
}

(Note: this would require threading isResourceCollapsedRef into useChat or checking collapse state inside handleResourceEvent before deciding to skip.)

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 2 potential issues.

Fix All in Cursor

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

@TheodoreSpeaks TheodoreSpeaks merged commit b7b575c into staging Mar 15, 2026
12 checks passed
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