Skip to content

Conversation

@87fmartin
Copy link

No description provided.

Comment on lines +151 to +240
minimumViews = 0,
minimumCompletionRate = 0,
displayLimit = 10,
}) => {
const router = useRouter();
const [selectedDifficulty, setSelectedDifficulty] =
useState(difficultyFilter);
const [selectedSortMethod, setSelectedSortMethod] = useState(sortBy);
const [hoveredRow, setHoveredRow] = useState(null);

const processedData = pageViewData
// Filter by sections (based on the first segment of the path)
.filter((page) => {
const pageSection = page.path.split('/')[1];
return sections.includes(pageSection);
})
// Filter by difficulty if specified
.filter((page) => {
return (
selectedDifficulty === 'all' || page.difficulty === selectedDifficulty
);
})
// Optionally filter out outdated content
.filter((page) => {
if (includeOutdated) return true;
const lastUpdated = new Date(page.lastUpdated);
const sixMonthsAgo = new Date();
sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
return lastUpdated > sixMonthsAgo;
})
// Apply metric thresholds
.filter((page) => {
if (minimumViews > 0 && page.views < minimumViews) return false;
if (
minimumCompletionRate > 0 &&
page.completionRate < minimumCompletionRate / 100
)
return false;
return true;
})
// Compute an engagement score for demonstration purposes
.map((page) => {
const viewsNorm = Math.min(page.views / 250000, 1);
const completionNorm = page.completionRate;
const timeSpentNorm = Math.min(page.avgTimeSpent / 600, 1);

// Freshness factor computed from last update date
const lastUpdated = new Date(page.lastUpdated);
const monthsOld = (new Date() - lastUpdated) / (30 * 24 * 60 * 60 * 1000);
const freshnessScore = Math.max(0, 1 - monthsOld / 24);

// Difficulty multiplier for weighting
let difficultyMultiplier = 1;
if (page.difficulty === 'beginner') difficultyMultiplier = 0.9;
if (page.difficulty === 'intermediate') difficultyMultiplier = 1.0;
if (page.difficulty === 'advanced') difficultyMultiplier = 1.2;

// Compute weighted engagement score
const engagementScore =
(0.4 * viewsNorm +
0.3 * completionNorm +
0.2 * timeSpentNorm +
0.1 * Math.pow(freshnessScore, 2)) *
difficultyMultiplier;

return {
...page,
engagementScore,
};
})
// Sort the computed results
.sort((a, b) => {
let comparison = 0;
if (selectedSortMethod === 'views') {
comparison = b.views - a.views;
} else if (selectedSortMethod === 'completion') {
comparison = b.completionRate - a.completionRate;
} else if (selectedSortMethod === 'timeSpent') {
comparison = b.avgTimeSpent - a.avgTimeSpent;
} else if (selectedSortMethod === 'engagement') {
comparison = b.engagementScore - a.engagementScore;
} else if (selectedSortMethod === 'lastUpdated') {
comparison = new Date(b.lastUpdated) - new Date(a.lastUpdated);
}
return sortOrder === 'desc' ? comparison : -comparison;
})
// Limit the results to displayLimit entries.
.slice(0, displayLimit);

const filterControls = (

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The computation of processedData involves multiple chained array operations (filter, map, sort) directly within the component function body. These operations are re-executed on every render. For potentially large datasets or frequent re-renders (e.g., due to state changes in selectedDifficulty or selectedSortMethod), this can lead to performance degradation. Memoizing processedData using React.useMemo will optimize performance by caching the result and recomputing it only when its dependencies change.

Suggested change
minimumViews = 0,
minimumCompletionRate = 0,
displayLimit = 10,
}) => {
const router = useRouter();
const [selectedDifficulty, setSelectedDifficulty] =
useState(difficultyFilter);
const [selectedSortMethod, setSelectedSortMethod] = useState(sortBy);
const [hoveredRow, setHoveredRow] = useState(null);
const processedData = pageViewData
// Filter by sections (based on the first segment of the path)
.filter((page) => {
const pageSection = page.path.split('/')[1];
return sections.includes(pageSection);
})
// Filter by difficulty if specified
.filter((page) => {
return (
selectedDifficulty === 'all' || page.difficulty === selectedDifficulty
);
})
// Optionally filter out outdated content
.filter((page) => {
if (includeOutdated) return true;
const lastUpdated = new Date(page.lastUpdated);
const sixMonthsAgo = new Date();
sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
return lastUpdated > sixMonthsAgo;
})
// Apply metric thresholds
.filter((page) => {
if (minimumViews > 0 && page.views < minimumViews) return false;
if (
minimumCompletionRate > 0 &&
page.completionRate < minimumCompletionRate / 100
)
return false;
return true;
})
// Compute an engagement score for demonstration purposes
.map((page) => {
const viewsNorm = Math.min(page.views / 250000, 1);
const completionNorm = page.completionRate;
const timeSpentNorm = Math.min(page.avgTimeSpent / 600, 1);
// Freshness factor computed from last update date
const lastUpdated = new Date(page.lastUpdated);
const monthsOld = (new Date() - lastUpdated) / (30 * 24 * 60 * 60 * 1000);
const freshnessScore = Math.max(0, 1 - monthsOld / 24);
// Difficulty multiplier for weighting
let difficultyMultiplier = 1;
if (page.difficulty === 'beginner') difficultyMultiplier = 0.9;
if (page.difficulty === 'intermediate') difficultyMultiplier = 1.0;
if (page.difficulty === 'advanced') difficultyMultiplier = 1.2;
// Compute weighted engagement score
const engagementScore =
(0.4 * viewsNorm +
0.3 * completionNorm +
0.2 * timeSpentNorm +
0.1 * Math.pow(freshnessScore, 2)) *
difficultyMultiplier;
return {
...page,
engagementScore,
};
})
// Sort the computed results
.sort((a, b) => {
let comparison = 0;
if (selectedSortMethod === 'views') {
comparison = b.views - a.views;
} else if (selectedSortMethod === 'completion') {
comparison = b.completionRate - a.completionRate;
} else if (selectedSortMethod === 'timeSpent') {
comparison = b.avgTimeSpent - a.avgTimeSpent;
} else if (selectedSortMethod === 'engagement') {
comparison = b.engagementScore - a.engagementScore;
} else if (selectedSortMethod === 'lastUpdated') {
comparison = new Date(b.lastUpdated) - new Date(a.lastUpdated);
}
return sortOrder === 'desc' ? comparison : -comparison;
})
// Limit the results to displayLimit entries.
.slice(0, displayLimit);
const filterControls = (
const processedData = React.useMemo(() => {
return pageViewData
// Filter by sections (based on the first segment of the path)
.filter((page) => {
const pageSection = page.path.split('/')[1];
return sections.includes(pageSection);
})
// Filter by difficulty if specified
.filter((page) => {
return (
selectedDifficulty === 'all' || page.difficulty === selectedDifficulty
);
})
// Optionally filter out outdated content
.filter((page) => {
if (includeOutdated) return true;
const lastUpdated = new Date(page.lastUpdated);
const sixMonthsAgo = new Date();
sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
return lastUpdated > sixMonthsAgo;
})
// Apply metric thresholds
.filter((page) => {
if (minimumViews > 0 && page.views < minimumViews) return false;
if (
minimumCompletionRate > 0 &&
page.completionRate < minimumCompletionRate / 100
)
return false;
return true;
})
// Compute an engagement score for demonstration purposes
.map((page) => {
const viewsNorm = Math.min(page.views / 250000, 1);
const completionNorm = page.completionRate;
const timeSpentNorm = Math.min(page.avgTimeSpent / 600, 1);
// Freshness factor computed from last update date
const lastUpdated = new Date(page.lastUpdated);
const monthsOld = (new Date() - lastUpdated) / (30 * 24 * 60 * 60 * 1000);
const freshnessScore = Math.max(0, 1 - monthsOld / 24);
// Difficulty multiplier for weighting
let difficultyMultiplier = 1;
if (page.difficulty === 'beginner') difficultyMultiplier = 0.9;
if (page.difficulty === 'intermediate') difficultyMultiplier = 1.0;
if (page.difficulty === 'advanced') difficultyMultiplier = 1.2;
// Compute weighted engagement score
const engagementScore =
(0.4 * viewsNorm +
0.3 * completionNorm +
0.2 * timeSpentNorm +
0.1 * Math.pow(freshnessScore, 2)) *
difficultyMultiplier;
return {
...page,
engagementScore,
};
})
// Sort the computed results
.sort((a, b) => {
let comparison = 0;
if (selectedSortMethod === 'views') {
comparison = b.views - a.views;
} else if (selectedSortMethod === 'completion') {
comparison = b.completionRate - a.completionRate;
} else if (selectedSortMethod === 'timeSpent') {
comparison = b.avgTimeSpent - a.avgTimeSpent;
} else if (selectedSortMethod === 'engagement') {
comparison = b.engagementScore - a.engagementScore;
} else if (selectedSortMethod === 'lastUpdated') {
comparison = new Date(b.lastUpdated) - new Date(a.lastUpdated);
}
return sortOrder === 'desc' ? comparison : -comparison;
})
// Limit the results to displayLimit entries.
.slice(0, displayLimit);
}, [sections, selectedDifficulty, includeOutdated, minimumViews, minimumCompletionRate, selectedSortMethod, sortOrder, displayLimit]);

Review by Conductor

Comment on lines +563 to +566
<div style={{textAlign: 'center', marginTop: '20px'}}>
<img
src="/images/meta-gradient.png"
style={{height: '3px', width: '50%'}}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The <img> tag is missing width and height attributes. These attributes are crucial for preventing Cumulative Layout Shift (CLS) because they allow the browser to reserve space for the image before it loads. Additionally, an alt attribute should be provided for accessibility; use alt="" for decorative images.

Suggested change
<div style={{textAlign: 'center', marginTop: '20px'}}>
<img
src="/images/meta-gradient.png"
style={{height: '3px', width: '50%'}}
<img
src="/images/meta-gradient.png"
width="200"
height="3"
alt=""
style={{height: '3px', width: '50%'}}
/>

Review by Conductor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants