useDebounceEffect 是 antd-mobile 提供的一个自定义 Hook,用于在 React 函数组件中实现防抖效果的副作用。它基于 useEffect 和 lodash 的 debounce 函数封装。
import { useDebounceEffect } from 'antd-mobile'
import React, { useState } from 'react'
function SearchComponent() {
const [keyword, setKeyword] = useState('')
const [searchResults, setSearchResults] = useState([])
useDebounceEffect(() => {
// 这个函数会在 keyword 停止变化 500ms 后执行
if (keyword.trim()) {
fetchResults(keyword)
}
}, [keyword], { wait: 500 })
const fetchResults = async (query) => {
const response = await fetch(`/api/search?q=${query}`)
const data = await response.json()
setSearchResults(data)
}
return (
<div>
<input
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
placeholder="搜索..."
/>
<ul>
{searchResults.map(result => (
<li key={result.id}>{result.name}</li>
))}
</ul>
</div>
)
}
useDebounceEffect(
effect: () => void | (() => void), // 要执行的副作用函数
deps: any[], // 依赖数组
options: {
wait?: number, // 等待时间,默认 1000ms
leading?: boolean, // 是否在延迟开始前调用
trailing?: boolean, // 是否在延迟结束后调用
maxWait?: number // 允许被延迟的最大值
}
)
function FormValidation() {
const [email, setEmail] = useState('')
const [validationError, setValidationError] = useState('')
useDebounceEffect(() => {
if (email) {
validateEmail(email)
}
}, [email], { wait: 300 })
const validateEmail = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailRegex.test(email)) {
setValidationError('请输入有效的邮箱地址')
} else {
setValidationError('')
}
}
return (
<div>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="请输入邮箱"
/>
{validationError && <div style={{ color: 'red' }}>{validationError}</div>}
</div>
)
}
function ResponsiveComponent() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight
})
useDebounceEffect(() => {
const handleResize = () => {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight
})
}
window.addEventListener('resize', handleResize)
// 清理函数
return () => {
window.removeEventListener('resize', handleResize)
}
}, [], { wait: 150 }) // 依赖数组为空,只会在挂载和卸载时执行
return (
<div>
窗口尺寸: {windowSize.width} x {windowSize.height}
</div>
)
}
function DraftEditor() {
const [content, setContent] = useState('')
const [saved, setSaved] = useState(false)
useDebounceEffect(() => {
if (content.trim()) {
saveDraft(content)
}
}, [content], {
wait: 1000,
trailing: true // 确保延迟结束后一定会执行
})
const saveDraft = async (text) => {
try {
await fetch('/api/save-draft', {
method: 'POST',
body: JSON.stringify({ content: text })
})
setSaved(true)
setTimeout(() => setSaved(false), 2000)
} catch (error) {
console.error('保存失败:', error)
}
}
return (
<div>
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
placeholder="输入内容..."
rows={10}
/>
{saved && <div style={{ color: 'green' }}>已自动保存</div>}
</div>
)
}
function AutoComplete() {
const [query, setQuery] = useState('')
const [suggestions, setSuggestions] = useState([])
useDebounceEffect(() => {
let isCancelled = false
const fetchSuggestions = async () => {
if (!query.trim()) {
setSuggestions([])
return
}
try {
const response = await fetch(`/api/suggestions?q=${query}`)
const data = await response.json()
if (!isCancelled) {
setSuggestions(data)
}
} catch (error) {
if (!isCancelled) {
console.error('获取建议失败:', error)
}
}
}
fetchSuggestions()
// 清理函数,防止组件卸载后仍设置状态
return () => {
isCancelled = true
}
}, [query], { wait: 300 })
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="搜索..."
/>
<ul>
{suggestions.map(suggestion => (
<li key={suggestion.id}>{suggestion.text}</li>
))}
</ul>
</div>
)
}
useEffect 一样,必须正确声明依赖项
性能优化:适当设置 wait 时间,避免过于频繁的执行
Leading/Trailing:根据需求选择合适的调用时机
// 普通 useEffect - 每次 keyword 变化都会立即执行
useEffect(() => {
search(keyword)
}, [keyword])
// useDebounceEffect - keyword 停止变化 wait 毫秒后执行
useDebounceEffect(() => {
search(keyword)
}, [keyword], { wait: 500 })
useDebounceEffect 特别适合处理高频触发但又不需要立即响应的场景,如搜索建议、表单验证、自动保存等。