Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2024-05-09 - Extracted Static Arrays to Prevent Unnecessary Renders
**Learning:** In components like `Documentaries.jsx`, static configuration arrays (like `externalResources`) were defined directly inside the component body. This causes them to be recreated on every single render, which completely breaks referential equality checks for downstream hooks like `useMemo` or child components wrapped in `React.memo`, leading to subtle and frequent performance issues.
**Action:** When attempting to memoize derived state or optimize components, always inspect if there are locally defined objects or arrays being used as dependencies. If they don't depend on component state or props, explicitly move them outside the component definition to ensure a stable reference before applying `useMemo`.
204 changes: 103 additions & 101 deletions src/pages/Documentaries.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useState, useMemo } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faBook,
Expand Down Expand Up @@ -26,6 +26,97 @@ import ErrorMessage from "../components/common/ErrorMessage";
import SkeletonLoader from "../components/common/SkeletonLoader";
import Button from "../components/common/Button";

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");
Expand Down Expand Up @@ -58,108 +149,19 @@ const Documentaries = () => {
},
];

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) {
Expand Down
24 changes: 13 additions & 11 deletions src/pages/Forum.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useMemo } from 'react';
import { Link } from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
Expand Down Expand Up @@ -91,16 +91,18 @@ const Forum = () => {
}
];

const filteredPosts = posts?.filter(post => {
const matchesSearch = !searchTerm ||
post.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
post.content.toLowerCase().includes(searchTerm.toLowerCase());

const matchesCategory = selectedCategory === 'all' ||
(forums && forums.some(forum => forum.id === post.forum_id && forum.title.toLowerCase().includes(selectedCategory)));

return matchesSearch && matchesCategory;
}) || [];
const filteredPosts = useMemo(() => {
return posts?.filter(post => {
const matchesSearch = !searchTerm ||
post.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
post.content.toLowerCase().includes(searchTerm.toLowerCase());

const matchesCategory = selectedCategory === 'all' ||
(forums && forums.some(forum => forum.id === post.forum_id && forum.title.toLowerCase().includes(selectedCategory)));

return matchesSearch && matchesCategory;
}) || [];
}, [posts, searchTerm, selectedCategory, forums]);

const categories = [
{ id: 'all', name: 'Semua Diskusi', icon: faComments },
Expand Down
50 changes: 26 additions & 24 deletions src/pages/Roadmap.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useMemo } from 'react';
import { Link } from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
Expand Down Expand Up @@ -62,29 +62,31 @@ const Roadmap = () => {
return 'from-gray-500 to-gray-600';
};

const filteredRoadmaps = roadmaps?.filter(roadmap => {
if (selectedCategory === 'all') return true;
const pathName = roadmap.career_path.toLowerCase();

switch (selectedCategory) {
case 'web':
return pathName.includes('web') || pathName.includes('frontend') || pathName.includes('fullstack');
case 'mobile':
return pathName.includes('mobile');
case 'data':
return pathName.includes('data') || pathName.includes('scientist');
case 'devops':
return pathName.includes('devops');
case 'security':
return pathName.includes('security');
case 'design':
return pathName.includes('design') || pathName.includes('ui');
case 'ai':
return pathName.includes('machine') || pathName.includes('ai');
default:
return true;
}
}) || [];
const filteredRoadmaps = useMemo(() => {
return roadmaps?.filter(roadmap => {
if (selectedCategory === 'all') return true;
const pathName = roadmap.career_path.toLowerCase();

switch (selectedCategory) {
case 'web':
return pathName.includes('web') || pathName.includes('frontend') || pathName.includes('fullstack');
case 'mobile':
return pathName.includes('mobile');
case 'data':
return pathName.includes('data') || pathName.includes('scientist');
case 'devops':
return pathName.includes('devops');
case 'security':
return pathName.includes('security');
case 'design':
return pathName.includes('design') || pathName.includes('ui');
case 'ai':
return pathName.includes('machine') || pathName.includes('ai');
default:
return true;
}
}) || [];
}, [roadmaps, selectedCategory]);

if (error) {
return (
Expand Down