Skip to content

Commit 428b220

Browse files
committed
Refactor blog filtering logic for maintainability
Refactored the blogs page filtering logic to improve maintainability and scalability. The filtering logic was extracted into a reusable utility function, and the blogs page now derives filtered results with [useMemo] instead of storing duplicated filtered state in [useEffect]. This keeps the component simpler and makes it easier to extend search and filter features in the future. Validation: type-checked the touched files and confirmed there are no errors.
1 parent 152d7a0 commit 428b220

2 files changed

Lines changed: 34 additions & 24 deletions

File tree

src/pages/blogs/index.tsx

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,28 @@
1-
import React, { type ChangeEvent } from "react";
1+
import React from "react";
22
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
33
import Layout from "@theme/Layout";
44
import Link from "@docusaurus/Link";
55
import blogs from "../../database/blogs/index";
66
import Head from "@docusaurus/Head";
77
import { getAuthorProfiles, getAuthorTooltip } from "../../utils/authors";
8+
import { filterBlogsBySearchTerm } from "../../utils/blogFilters";
89

910
import "./blogs-new.css";
1011

1112
export default function Blogs() {
1213
const { siteConfig } = useDocusaurusContext();
1314
const [searchInput, setSearchInput] = React.useState("");
1415
const [searchTerm, setSearchTerm] = React.useState("");
15-
const [filteredBlogs, setFilteredBlogs] = React.useState(blogs);
16-
17-
// Filter blogs after the user submits the blog search form.
18-
React.useEffect(() => {
19-
let filtered = blogs;
20-
21-
if (searchTerm.trim() !== "") {
22-
const normalizedSearch = searchTerm.trim().toLowerCase();
23-
filtered = filtered.filter(
24-
(blog) =>
25-
blog.title.toLowerCase().includes(normalizedSearch) ||
26-
blog.description.toLowerCase().includes(normalizedSearch) ||
27-
blog.tags?.some((tag) =>
28-
tag.toLowerCase().includes(normalizedSearch),
29-
),
30-
);
31-
}
32-
33-
setFilteredBlogs(filtered);
34-
}, [searchTerm]);
16+
const filteredBlogs = React.useMemo(
17+
() => filterBlogsBySearchTerm(blogs, searchTerm),
18+
[searchTerm],
19+
);
3520

36-
const handleSearchChange = (e: ChangeEvent<HTMLInputElement>) => {
21+
const handleSearchChange = (e: { target: { value: string } }) => {
3722
setSearchInput(e.target.value);
3823
};
3924

40-
const handleSearchSubmit = (e: React.FormEvent<HTMLFormElement>) => {
25+
const handleSearchSubmit = (e: { preventDefault: () => void }) => {
4126
e.preventDefault();
4227
setSearchTerm(searchInput.trim());
4328
};
@@ -157,7 +142,9 @@ export default function Blogs() {
157142
<div className="articles-grid">
158143
{filteredBlogs.length > 0 ? (
159144
filteredBlogs.map((blog) => (
160-
<BlogCard key={blog.id ?? blog.slug} blog={blog} />
145+
<React.Fragment key={blog.id ?? blog.slug}>
146+
<BlogCard blog={blog} />
147+
</React.Fragment>
161148
))
162149
) : (
163150
<div className="no-results">

src/utils/blogFilters.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
type BlogLike = {
2+
title: string;
3+
description: string;
4+
tags?: string[];
5+
};
6+
7+
export function filterBlogsBySearchTerm<T extends BlogLike>(
8+
blogs: T[],
9+
searchTerm: string,
10+
): T[] {
11+
const normalizedSearch = searchTerm.trim().toLowerCase();
12+
13+
if (normalizedSearch === "") {
14+
return blogs;
15+
}
16+
17+
return blogs.filter(
18+
(blog) =>
19+
blog.title.toLowerCase().includes(normalizedSearch) ||
20+
blog.description.toLowerCase().includes(normalizedSearch) ||
21+
blog.tags?.some((tag) => tag.toLowerCase().includes(normalizedSearch)),
22+
);
23+
}

0 commit comments

Comments
 (0)