diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..10d76cf --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-07-01 - Optimizing React rendering with useMemo +**Learning:** Found an O(n) array sorting operation and deep recursive countFiles inside the body of a React Function Component (`TreeNode`) which is called many times for deeply nested trees. React components that are pure should memoize their expensive operations instead of doing them synchronously during each render pass. +**Action:** Always wrap `children` sorting and recursive tree traversal computations in `React.useMemo` to prevent deep performance degradation during re-renders. diff --git a/src/components/TreeNode.jsx b/src/components/TreeNode.jsx index e91a1d6..3186ed2 100644 --- a/src/components/TreeNode.jsx +++ b/src/components/TreeNode.jsx @@ -18,13 +18,14 @@ export const TreeNode = React.memo(function TreeNodeInner(props){ var node=props.node,selected=props.selected,onSelect=props.onSelect,expanded=props.expanded,toggle=props.toggle,filterFolder=props.filterFolder,activeFilter=props.activeFilter; var isOpen=expanded.has(node.path); var isFiltered=activeFilter===node.path; - var children=Object.values(node.children).sort(function(a,b){return a.name.localeCompare(b.name);}); + var children=React.useMemo(function(){return Object.values(node.children).sort(function(a,b){return a.name.localeCompare(b.name);});}, [node.children]); + var fileCount=React.useMemo(function(){return countFiles(node);}, [node]); return React.createElement('div',null, React.createElement('div',{className:'tree-folder'+(isFiltered?' filtered':''),onClick:function(){if(node.path==='')filterFolder(null);else filterFolder(node.path);}}, React.createElement('span',{className:'tree-toggle'+(isOpen?' open':''),onClick:function(e){e.stopPropagation();toggle(node.path);}},children.length>0||node.files.length>0?'▶':''), React.createElement(Icon,{name:isOpen?'folder-open':'folder',size:'m',className:'tree-entry-icon'}), React.createElement('span',{className:'tree-name'},node.name), - React.createElement('span',{className:'tree-count'},countFiles(node)) + React.createElement('span',{className:'tree-count'},fileCount) ), isOpen&&React.createElement('div',{className:'tree-children'}, children.map(function(c){return React.createElement(TreeNode,{key:c.path,node:c,selected:selected,onSelect:onSelect,expanded:expanded,toggle:toggle,filterFolder:filterFolder,activeFilter:activeFilter});}),