Skip to content

perf(blog): derive search results with useMemo instead of useState+us…#1322

Merged
MODSetter merged 1 commit intoMODSetter:devfrom
guangyang1206:perf/blog-search-derive-results-usememo
Apr 29, 2026
Merged

perf(blog): derive search results with useMemo instead of useState+us…#1322
MODSetter merged 1 commit intoMODSetter:devfrom
guangyang1206:perf/blog-search-derive-results-usememo

Conversation

@guangyang1206
Copy link
Copy Markdown
Contributor

@guangyang1206 guangyang1206 commented Apr 29, 2026

…eEffect

Fixes #1246

Replace the useState/useEffect pattern that synced fuzzy search results into local state on every search or searcher change with a single useMemo that derives results directly during render.

Before:
const [results, setResults] = useState(allBlogs);
useEffect(() => {
setResults(searcher.search(search));
}, [search, searcher]);

After:
const gridItems = useMemo(() => {
const results = search.trim() ? searcher.search(search) : allBlogs;
...
}, [search, searcher, allBlogs, featuredSlug]);

This removes an extra re-render per keystroke and eliminates the stale intermediate state that occurred between the search input change and the effect firing.

Description

Closes #1246

Replace the useState + useEffect pattern in MagazineSearchGrid that synced fuzzy search results into local state on every search/searcher change with a single useMemo that derives filtered results directly during render.

Before:

const [results, setResults] = useState(allBlogs);
useEffect(() => {
  setResults(searcher.search(search));
}, [search, searcher]);

const gridItems = useMemo(() => {
  if (search.trim()) return results;
  return results.filter((b) => b.slug !== featuredSlug);
}, [results, search, featuredSlug]);

After:

const gridItems = useMemo(() => {
  const results = search.trim() ? searcher.search(search) : allBlogs;
  if (search.trim()) return results;
  return results.filter((b) => b.slug !== featuredSlug);
}, [search, searcher, allBlogs, featuredSlug]);

Benefits

  • Removes an extra re-render per keystroke (the effect fired after state change, causing a double render)
  • Eliminates stale intermediate state between search input change and effect firing
  • Simplifies the component by removing two state variables (results, setResults) and the useEffect

How Has This Been Tested?

Manually tested in the blog search input — search results filter correctly as you type, with no visible flash of stale results.

Type of Change

  • Performance improvement
  • Bug fix
  • New feature
  • Documentation update

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • No new warnings introduced

High-level PR Summary

This PR optimizes the blog search functionality by replacing a useState + useEffect pattern with a single useMemo hook to derive search results during render. This eliminates an unnecessary re-render on each keystroke and removes the stale intermediate state that occurred between search input changes and the effect firing, resulting in a more efficient and simpler component implementation.

⏱️ Estimated Review Time: 5-15 minutes

💡 Review Order Suggestion
Order File Path
1 surfsense_web/app/(home)/blog/blog-magazine.tsx

Need help? Join our Discord

…eEffect

Fixes MODSetter#1246

Replace the useState/useEffect pattern that synced fuzzy search results
into local state on every search or searcher change with a single useMemo
that derives results directly during render.

Before:
  const [results, setResults] = useState(allBlogs);
  useEffect(() => {
    setResults(searcher.search(search));
  }, [search, searcher]);

After:
  const gridItems = useMemo(() => {
    const results = search.trim() ? searcher.search(search) : allBlogs;
    ...
  }, [search, searcher, allBlogs, featuredSlug]);

This removes an extra re-render per keystroke and eliminates the stale
intermediate state that occurred between the search input change and the
effect firing.
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 29, 2026

@guangyang1206 is attempting to deploy a commit to the Rohan Verma's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0a8043cd-0793-4ae3-8e85-3c8a65cf3419

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@MODSetter MODSetter merged commit afb4b09 into MODSetter:dev Apr 29, 2026
1 of 3 checks passed
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.

2 participants