From 01fde32738ef81d3fda16f6dd7ed8c0521143f60 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 23:46:39 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20validation=20err?= =?UTF-8?q?or=20lookup=20in=20VisualEditor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaced O(N*M) array filtering with O(M) useMemo map and O(1) lookups to reduce render overhead when dealing with numerous form fields and validation rules. Co-authored-by: anchapin <6326294+anchapin@users.noreply.github.com> --- .jules/bolt.md | 8 ++++++++ .../BehaviorEditor/VisualEditor/VisualEditor.tsx | 16 ++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index b0f22698..73e5c8b8 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -6,3 +6,11 @@ ## 2026-03-18 - O(N) aggregation over O(N*M) filters **Learning:** In React components with dynamically generated filter lists, using `.filter().length` inside a `.map()` results in O(N*M) time complexity, leading to sluggish renders with larger datasets. **Action:** Use `.reduce()` or a single loop inside `useMemo` to pre-calculate category counts in an O(N) pass instead. + +## 2026-03-19 - [Avoid reduce and some for hot loops in React] +**Learning:** Using chained array methods like `.reduce` and multiple `.some` calls inside `useMemo` hooks can cause unnecessary allocations and performance degradation due to closures and object creations, especially when checking nested or multiple arrays. +**Action:** Use simple `for` loops inside `useMemo` to combine multiple conditions, breaking early and avoiding callback allocations for faster single-pass validation or aggregation. + +## 2026-03-19 - [Avoid O(N*M) lookups inside list renders] +**Learning:** Passing a callback that performs `.filter` on a full array down to child components that iterate over lists (like `FormBuilder` iterating over `fields` and calling `getFieldErrors(field.name)`) results in O(N*M) time complexity. +**Action:** Replace callback filters with a single pass O(M) `.reduce` or loop inside a `useMemo` to construct a grouped hash map by ID, turning child component lookups into O(1). diff --git a/frontend/src/components/BehaviorEditor/VisualEditor/VisualEditor.tsx b/frontend/src/components/BehaviorEditor/VisualEditor/VisualEditor.tsx index 5b931d4c..cb811fa4 100644 --- a/frontend/src/components/BehaviorEditor/VisualEditor/VisualEditor.tsx +++ b/frontend/src/components/BehaviorEditor/VisualEditor/VisualEditor.tsx @@ -136,12 +136,24 @@ export const VisualEditor: React.FC = ({ [fields, categories] ); + // ⚡ Bolt optimization: Group validation errors by field in a single O(M) pass + // instead of filtering the array inside a callback for every single field O(N*M). + const fieldErrorsMap = useMemo(() => { + return validationErrors.reduce((acc, error) => { + if (!acc[error.field]) { + acc[error.field] = []; + } + acc[error.field].push(error); + return acc; + }, {} as Record); + }, [validationErrors]); + // Get field errors const getFieldErrors = useCallback( (fieldName: string): ValidationRule[] => { - return validationErrors.filter((error) => error.field === fieldName); + return fieldErrorsMap[fieldName] || []; }, - [validationErrors] + [fieldErrorsMap] ); // Check if form has unsaved changes