From bb092b8261617a2033c731e1d4b73f80062a598f Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 20:46:15 +0000 Subject: [PATCH] perf: memoize filtered resources and extract static arrays in Documentaries Moved static `categories` and `externalResources` arrays outside of the `Documentaries` component to prevent recreation on every render. Wrapped the `filteredResources` calculation in a `useMemo` hook to avoid expensive recalculation when unrelated state changes. Co-authored-by: belpythons <187399139+belpythons@users.noreply.github.com> --- .jules/bolt.md | 3 + src/pages/Documentaries.jsx | 246 ++++++++++++++++++------------------ 2 files changed, 127 insertions(+), 122 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..dd4ad6f --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-05-24 - Unnecessary Recalculation on Re-render +**Learning:** This codebase frequently performs client-side filtering on static datasets (like `externalResources` or `categories`) directly inside component bodies. If these datasets are defined *inside* the component, they are recreated on every render, causing referential inequality that breaks React optimizations. Even if moved outside, the filtering operation itself re-runs on every state change (e.g., typing in a search bar), which can be computationally expensive if the dataset grows. +**Action:** When encountering components with derived filtered state (like list views or directories), always move static source arrays outside the component definition. Then, wrap the filtering logic in a `useMemo` hook with strict dependencies on the specific filter states to prevent expensive recalculations and unnecessary re-renders. diff --git a/src/pages/Documentaries.jsx b/src/pages/Documentaries.jsx index 8befef4..cd14b95 100644 --- a/src/pages/Documentaries.jsx +++ b/src/pages/Documentaries.jsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useMemo } from "react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faBook, @@ -26,6 +26,118 @@ import ErrorMessage from "../components/common/ErrorMessage"; import SkeletonLoader from "../components/common/SkeletonLoader"; import Button from "../components/common/Button"; +const categories = [ + { id: "all", name: "All Resources", icon: faBook }, + { id: "html", name: "HTML", icon: faHtml5, color: "text-orange-600" }, + { id: "css", name: "CSS", icon: faCss3Alt, color: "text-blue-600" }, + { + id: "javascript", + name: "JavaScript", + icon: faJsSquare, + color: "text-yellow-600", + }, + { id: "python", name: "Python", icon: faPython, color: "text-green-600" }, + { id: "react", name: "React", icon: faReact, color: "text-cyan-600" }, + { id: "node", name: "Node.js", icon: faNodeJs, color: "text-green-500" }, + { + id: "database", + name: "Database", + icon: faDatabase, + color: "text-purple-600", + }, +]; + +const externalResources = [ + { + id: 1, + title: "W3Schools HTML Tutorial", + description: "Complete HTML tutorial with examples and exercises", + url: "https://www.w3schools.com/html/", + category: "html", + type: "Tutorial", + difficulty: "Beginner", + icon: faHtml5, + color: "bg-orange-100 text-orange-600", + }, + { + id: 2, + title: "W3Schools CSS Tutorial", + description: "Learn CSS from basic to advanced with interactive examples", + url: "https://www.w3schools.com/css/", + category: "css", + type: "Tutorial", + difficulty: "Beginner", + icon: faCss3Alt, + color: "bg-blue-100 text-blue-600", + }, + { + id: 3, + title: "JavaScript Guide - MDN", + description: "Comprehensive JavaScript documentation and guide", + url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide", + category: "javascript", + type: "Documentation", + difficulty: "Intermediate", + icon: faJsSquare, + color: "bg-yellow-100 text-yellow-600", + }, + { + id: 4, + title: "Python Official Tutorial", + description: "Official Python tutorial from python.org", + url: "https://docs.python.org/3/tutorial/", + category: "python", + type: "Official Docs", + difficulty: "Beginner", + icon: faPython, + color: "bg-green-100 text-green-600", + }, + { + id: 5, + title: "React Documentation", + description: "Official React documentation and learning resources", + url: "https://react.dev/", + category: "react", + type: "Official Docs", + difficulty: "Intermediate", + icon: faReact, + color: "bg-cyan-100 text-cyan-600", + }, + { + id: 6, + title: "Node.js Getting Started", + description: "Official Node.js guides and API documentation", + url: "https://nodejs.org/en/learn/getting-started/introduction-to-nodejs", + category: "node", + type: "Official Docs", + difficulty: "Intermediate", + icon: faNodeJs, + color: "bg-green-100 text-green-500", + }, + { + id: 7, + title: "SQL Tutorial - W3Schools", + description: "Learn SQL for database management", + url: "https://www.w3schools.com/sql/", + category: "database", + type: "Tutorial", + difficulty: "Beginner", + icon: faDatabase, + color: "bg-purple-100 text-purple-600", + }, + { + id: 8, + title: "Docker Get Started", + description: "Official Docker tutorial and documentation", + url: "https://docs.docker.com/get-started/", + category: "devops", + type: "Official Docs", + difficulty: "Intermediate", + icon: faDocker, + color: "bg-blue-100 text-blue-500", + }, +]; + const Documentaries = () => { const [searchTerm, setSearchTerm] = useState(""); const [selectedCategory, setSelectedCategory] = useState("all"); @@ -37,129 +149,19 @@ const Documentaries = () => { refetch, } = useApi(documentariesApi.getAll); - const categories = [ - { id: "all", name: "All Resources", icon: faBook }, - { id: "html", name: "HTML", icon: faHtml5, color: "text-orange-600" }, - { id: "css", name: "CSS", icon: faCss3Alt, color: "text-blue-600" }, - { - id: "javascript", - name: "JavaScript", - icon: faJsSquare, - color: "text-yellow-600", - }, - { id: "python", name: "Python", icon: faPython, color: "text-green-600" }, - { id: "react", name: "React", icon: faReact, color: "text-cyan-600" }, - { id: "node", name: "Node.js", icon: faNodeJs, color: "text-green-500" }, - { - id: "database", - name: "Database", - icon: faDatabase, - color: "text-purple-600", - }, - ]; - - const externalResources = [ - { - id: 1, - title: "W3Schools HTML Tutorial", - description: "Complete HTML tutorial with examples and exercises", - url: "https://www.w3schools.com/html/", - category: "html", - type: "Tutorial", - difficulty: "Beginner", - icon: faHtml5, - color: "bg-orange-100 text-orange-600", - }, - { - id: 2, - title: "W3Schools CSS Tutorial", - description: "Learn CSS from basic to advanced with interactive examples", - url: "https://www.w3schools.com/css/", - category: "css", - type: "Tutorial", - difficulty: "Beginner", - icon: faCss3Alt, - color: "bg-blue-100 text-blue-600", - }, - { - id: 3, - title: "JavaScript Guide - MDN", - description: "Comprehensive JavaScript documentation and guide", - url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide", - category: "javascript", - type: "Documentation", - difficulty: "Intermediate", - icon: faJsSquare, - color: "bg-yellow-100 text-yellow-600", - }, - { - id: 4, - title: "Python Official Tutorial", - description: "Official Python tutorial from python.org", - url: "https://docs.python.org/3/tutorial/", - category: "python", - type: "Official Docs", - difficulty: "Beginner", - icon: faPython, - color: "bg-green-100 text-green-600", - }, - { - id: 5, - title: "React Documentation", - description: "Official React documentation and learning resources", - url: "https://react.dev/", - category: "react", - type: "Official Docs", - difficulty: "Intermediate", - icon: faReact, - color: "bg-cyan-100 text-cyan-600", - }, - { - id: 6, - title: "Node.js Getting Started", - description: "Official Node.js guides and API documentation", - url: "https://nodejs.org/en/learn/getting-started/introduction-to-nodejs", - category: "node", - type: "Official Docs", - difficulty: "Intermediate", - icon: faNodeJs, - color: "bg-green-100 text-green-500", - }, - { - id: 7, - title: "SQL Tutorial - W3Schools", - description: "Learn SQL for database management", - url: "https://www.w3schools.com/sql/", - category: "database", - type: "Tutorial", - difficulty: "Beginner", - icon: faDatabase, - color: "bg-purple-100 text-purple-600", - }, - { - id: 8, - title: "Docker Get Started", - description: "Official Docker tutorial and documentation", - url: "https://docs.docker.com/get-started/", - category: "devops", - type: "Official Docs", - difficulty: "Intermediate", - icon: faDocker, - color: "bg-blue-100 text-blue-500", - }, - ]; - - const filteredResources = externalResources.filter((resource) => { - const matchesSearch = - !searchTerm || - resource.title.toLowerCase().includes(searchTerm.toLowerCase()) || - resource.description.toLowerCase().includes(searchTerm.toLowerCase()); + const filteredResources = useMemo(() => { + return externalResources.filter((resource) => { + const matchesSearch = + !searchTerm || + resource.title.toLowerCase().includes(searchTerm.toLowerCase()) || + resource.description.toLowerCase().includes(searchTerm.toLowerCase()); - const matchesCategory = - selectedCategory === "all" || resource.category === selectedCategory; + const matchesCategory = + selectedCategory === "all" || resource.category === selectedCategory; - return matchesSearch && matchesCategory; - }); + return matchesSearch && matchesCategory; + }); + }, [searchTerm, selectedCategory]); const getDifficultyColor = (difficulty) => { switch (difficulty) {