Skip to content

⚡ Bolt: Memoize TreeNode in FileExplorer#58

Merged
iotserver24 merged 1 commit intomainfrom
bolt-memoize-file-explorer-10416349430217688330
Apr 29, 2026
Merged

⚡ Bolt: Memoize TreeNode in FileExplorer#58
iotserver24 merged 1 commit intomainfrom
bolt-memoize-file-explorer-10416349430217688330

Conversation

@iotserver24
Copy link
Copy Markdown
Owner

@iotserver24 iotserver24 commented Apr 28, 2026

💡 What: Refactored TreeNode to use fine-grained Zustand selectors directly inside the recursive component instead of relying on top-down prop drilling of expandedPaths and selectedPath. Wrapped the component in React.memo().

🎯 Why: Previously, any file selection or folder toggle caused the expandedPaths or selectedPath object to change at the root of FileExplorer. Because TreeNode was recursively prop-drilled, this caused every single file and folder component (O(N)) to re-render, creating a severe performance bottleneck for large codebases.

📊 Impact: Reduces FileExplorer re-renders from O(N) (entire tree) to O(1) (only the specific node being selected/toggled).

🔬 Measurement: Open a project with many files. Click on a deeply nested file. Use the React Profiler to verify that only the single selected node re-renders, rather than the entire component tree.


PR created automatically by Jules for task 10416349430217688330 started by @iotserver24

Summary by CodeRabbit

Release Notes

  • Documentation

    • Updated development guidelines for optimizing recursive UI component rendering.
  • Refactor

    • Optimized FileExplorer component for improved performance and reduced re-renders when navigating file trees.

- Refactored `FileExplorer` to no longer prop-drill `expandedPaths` and `selectedPath` sets.
- Subscribed `TreeNode` directly to specific Zustand selectors `useFileStore(state => state.expandedPaths.has(node.path))` and `useFileStore(state => state.selectedPath === node.path)`.
- Wrapped `TreeNode` in `React.memo` to achieve O(1) targeted rendering for specific nodes during file selection and directory toggling.
@google-labs-jules
Copy link
Copy Markdown

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@xibe-review
Copy link
Copy Markdown

xibe-review Bot commented Apr 28, 2026

Hey @iotserver24! 👋

I'll go through the changes and help you out with an automated review! 🔍

Starting the review now...

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 452ce7bb-4bc7-40c3-933e-fe95bab0a451

📥 Commits

Reviewing files that changed from the base of the PR and between be4aacf and 54ff918.

📒 Files selected for processing (2)
  • .jules/bolt.md
  • webui/src/components/FileExplorer/index.tsx

📝 Walkthrough

Walkthrough

This pull request updates Zustand state management patterns in the FileExplorer component by replacing broad state reads with granular selectors, memoizing the TreeNode component to subscribe per-node, and relocating click handling logic into child components for optimization.

Changes

Cohort / File(s) Summary
Documentation Update
.jules/bolt.md
Adds guidance on minimizing re-render cascades in recursive UI by using fine-grained Zustand selectors localized to child components instead of top-down prop drilling.
FileExplorer Refactoring
webui/src/components/FileExplorer/index.tsx
Refactors to use granular Zustand selectors per-node instead of single destructured store, memoizes TreeNode component with per-node subscriptions to expandedPaths and selectedPath, moves click handling logic (node selection, expansion toggling, file reading) into TreeNode via useCallback, and updates effect dependencies.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A rabbit hops through selector lands,
Fine-grained state now in capable hands,
Each TreeNode memos its own little way,
Re-renders cascade less, hip-hip-hooray!
Props untangled, the tree stands so bright,

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bolt-memoize-file-explorer-10416349430217688330

Review rate limit: 2/3 reviews remaining, refill in 20 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@iotserver24 iotserver24 marked this pull request as ready for review April 29, 2026 01:41
Copilot AI review requested due to automatic review settings April 29, 2026 01:41
@iotserver24 iotserver24 merged commit 5fd7764 into main Apr 29, 2026
8 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Improves FileExplorer rendering performance by eliminating prop-drilling of frequently-changing Zustand state into the recursive TreeNode, and memoizing TreeNode so only affected nodes update on selection/expand changes.

Changes:

  • Refactors FileExplorer to subscribe only to fileTree, isLoading, and refreshFileTree via fine-grained Zustand selectors.
  • Refactors TreeNode to read expandedPaths/selectedPath directly from the store and wraps it with React.memo, using useCallback for event handlers.
  • Adds a Bolt learning note documenting the O(N) re-render issue and the selector-based approach.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
webui/src/components/FileExplorer/index.tsx Moves selection/expansion subscriptions into TreeNode and memoizes it to avoid cascading re-renders.
.jules/bolt.md Records the performance learning/approach in Jules “bolt” notes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .jules/bolt.md
## 2026-04-25 - [Bounded Concurrency for fs.stat]
**Learning:** Even lightweight I/O operations like `fs.stat` require bounded concurrency (e.g., chunking promises into batches) when operating on an arbitrary or potentially large number of files. Unbounded `Promise.all` can still trigger OS-level 'EMFILE' (too many open files) errors because Node.js attempts to open all file descriptors simultaneously.
**Action:** Always apply bounded concurrency (like `CONCURRENCY_LIMIT = 20`) for any `fs` operations that iterate over directories or unknown list sizes, not just for reading file contents.
## 2024-04-28 - O(N) cascade re-renders in FileExplorer with Zustand\n**Learning:** Prop-drilling large state objects from Zustand to recursive UI components (like tree nodes) creates O(N) re-renders because every single component evaluates top-down when one state changes. React.memo requires complicated state logic to resolve if parents receive all state.\n**Action:** Extract large state reads into fine-grained Zustand selectors directly at the target child component. Zustand manages subscriptions per-component, achieving O(1) targeted updates.
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

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

This new entry is written as a single line with literal "\n" escape sequences, so it won’t render properly as Markdown. Please replace the literal escapes with real newlines and format it like the other entries (heading, Learning:, Action: on separate lines).

Copilot uses AI. Check for mistakes.
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