Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@
## 2024-05-24 - [React Component Memoization]
**Learning:** In React components like `WorkspaceHome`, when layout state or polling changes trigger parent re-renders, expensive child components like `EmailDetail` will also re-render unnecessarily if not memoized.
**Action:** Always consider `React.memo` for heavy child components that rely on stable props (like IDs) when the parent component has frequent unrelated state updates.
## 2025-02-12 - Reused Existing Cache Map for O(1) Lookups in React Component Graph

**Learning:** When displaying complex relationships (e.g. `NetworkGraph.tsx`), rendering related edge labels repeatedly triggers nested $O(N)$ inline array lookups (`nodes.find(...)`). Even though a `Map` was already pre-computed (`nodeMap`) higher up the component tree to handle labels efficiently, helper functions (`findNodeLabel`) were not utilizing it. This bypassed the intended caching strategy and degraded to $O(M * N)$.

**Action:** When working with rendering layers that frequently resolve relational data (like nodes and edges in a graph), verify that helper functions actively accept and utilize the pre-computed `Map` instead of re-iterating over the source arrays. This transforms the bottleneck from $O(M * N)$ back to $O(M)$ operations without breaking readability.
14 changes: 9 additions & 5 deletions frontend/src/components/NetworkGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ function titleText(value: unknown) {
return value == null ? '' : String(value).trim();
}

function findNodeLabel(nodes: Node[], id: number | string) {
function findNodeLabel(nodes: Node[], id: number | string, nodeMap?: Map<string | number, string>) {
if (nodeMap) {
const cached = nodeMap.get(String(id));
if (cached) return cached;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
const node = nodes.find((candidate) => graphIdEquals(candidate.id, id));
return String(node?.label ?? id);
}
Expand All @@ -138,8 +142,8 @@ function describeEdge(edge: Edge, nodes: Node[], nodeMap?: Map<string | number,
fromLabel = nodeMap.get(String(edge.from)) ?? String(edge.from);
toLabel = nodeMap.get(String(edge.to)) ?? String(edge.to);
} else {
fromLabel = findNodeLabel(nodes, edge.from);
toLabel = findNodeLabel(nodes, edge.to);
fromLabel = findNodeLabel(nodes, edge.from, nodeMap);
toLabel = findNodeLabel(nodes, edge.to, nodeMap);
}
const title = titleText(edge.title);
return title ? `${fromLabel} -> ${toLabel} (${title})` : `${fromLabel} -> ${toLabel}`;
Expand Down Expand Up @@ -213,7 +217,7 @@ export default function NetworkGraph() {
const selectNode = (nodeId: number | string) => {
setRelationshipOptionId('');
setNodeOptionId(String(nodeId));
setSelectedGraphDetail(`선택된 노드: ${findNodeLabel(nodes, nodeId)}`);
setSelectedGraphDetail(`선택된 노드: ${findNodeLabel(nodes, nodeId, nodeMap)}`);
setGraphActionStatus('그래프에서 노드를 선택했습니다.');
};

Expand Down Expand Up @@ -303,7 +307,7 @@ export default function NetworkGraph() {
if (!isGraphId(node.id)) return;
setRelationshipOptionId('');
setNodeOptionId(String(node.id));
setSelectedGraphDetail(`선택된 노드: ${findNodeLabel(nodes, node.id)}`);
setSelectedGraphDetail(`선택된 노드: ${findNodeLabel(nodes, node.id, nodeMap)}`);
setGraphActionStatus(status);
networkRef.current?.selectNodes?.([node.id]);
networkRef.current?.fit?.({ nodes: [node.id], animation: false });
Expand Down
Loading