setKeyword(e.target.value)}
placeholder="Search..."
/>
)
}
```
This pattern is suitable for listening to a certain state and automatically executing corresponding operations when it changes.
* * *
## Handling in Events vs useEffect
There are two ways to implement search filtering:
| Method | Implementation | Use Case |
| --- | --- | --- |
| Event Handler | Direct filtering in onChange | Simple logic, triggered in only one place |
| useEffect Response | useEffect depends on keyword | Multiple data sources may affect filtering results |
In blog scenarios, the search box input filtering logic is simple, so it's more appropriate to place it directly in event handlers or useMemo.
useEffect is more suitable for scenarios that need to synchronize with external systems (such as calling APIs, storing to localStorage).
* * *
## Debounce Optimization
If search is triggered on every key press (fine for local filtering, but wasteful for network requests), it causes a lot of waste.
The idea of Debounce: execute the operation after the user stops inputting for a period of time.
## Example
```javascript
import{ useState, useEffect } from 'react'
function useDebounce(value, delay =300){
const[debouncedValue, setDebouncedValue]= useState(value)
useEffect(()=>{
// Set timer: update debouncedValue after delay ms
const timer = setTimeout(()=>{
setDebouncedValue(value)
}, delay)
// Cleanup function: if value changes again within delay, clear the previous timer
return()=> clearTimeout(timer)
},[value, delay])
return debouncedValue
}
// Using debounce
function SearchInput(){
const[keyword, setKeyword]= useState('')
const debouncedKeyword = useDebounce(keyword,300)
// Only execute when the debounced value changes (e.g., send request)
useEffect(()=>{
if(debouncedKeyword){
console.log('Actually executing searchοΌ', debouncedKeyword)
// fetch(`/api/search?q=${debouncedKeyword}`)
}
},)
return(
setKeyword(e.target.value)}
placeholder="Search..."
/>
)
}
```
> Debounce principle: each input resets a 300ms timer, only when there's no new input within 300ms will the timer execute and debouncedValue will update. This prevents frequent search triggers when users type quickly.
* * *
## Hands-on: Add Real-time Search to the Blog
## Example
```javascript
// File path: src/pages/HomePage.jsx
import{ useState, useEffect, useMemo } from 'react'
import BlogCard from '../components/BlogCard'
import CategoryFilter from '../components/CategoryFilter'
function HomePage(){
const[articles, setArticles]= useState([])
const[isLoading, setIsLoading]= useState(true)
const[error, setError]= useState(null)
const[activeCategory, setActiveCategory]= useState('All')
const[keyword, setKeyword]= useState('')// Search keyword
// Load data
useEffect(()=>{
let cancelled =false
async function fetchPosts(){
setIsLoading(true)
try{
const res = await fetch('/posts.json')
const data = await res.json()
if(!cancelled) setArticles(data)
}catch(err){
if(!cancelled) setError(err.message)
}finally{
if(!cancelled) setIsLoading(false)
}
}
fetchPosts()
return()=>{ cancelled =true}
},[])
const categories = useMemo(()=>{
return['All', ...new Set(articles.map(a => a.category))]
},)
// Filter + Search: filter by category and keyword simultaneously
const filteredArticles = useCurrent search: {keyword}
YouTip