文章
nextjs表单提交防抖
use-debounce.ts
import { useEffect, useState } from "react"
export const useDebounce = (value: string, delay: number) => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => clearTimeout(timer);
}, [delay, value]);
return debouncedValue;
};
应用:
"use client";
import { useDocumentsStore } from '@/app/(protected)/dashboard/documents/_libs/use-document-store'
import { documentFiltersDefaultValues, documentFiltersSchema, DocumentFiltersSchema } from '@/app/(protected)/dashboard/documents/_types/document-filter-schema'
import { zodResolver } from '@hookform/resolvers/zod'
import { useEffect } from 'react'
import { FormProvider, useForm, useWatch } from 'react-hook-form'
import { useDebounce } from '@/lib/use-debounce'
import { ControlledInput } from '@/components/controlled/controlled-input'
function DocumentFiltersDrawer() {
const form = useForm<DocumentFiltersSchema>({
defaultValues: documentFiltersDefaultValues,
resolver: zodResolver(documentFiltersSchema)
})
const {
updateDocumentFiltersSearchTerm,
updateDocumentFiltersPage
} = useDocumentsStore();
const searchTerm = useWatch({ control: form.control, name: "searchTerm" });
const debouncedSearchTerm = useDebounce(searchTerm, 400);
useEffect(() => {
updateDocumentFiltersSearchTerm(debouncedSearchTerm);
updateDocumentFiltersPage(1);
}, [debouncedSearchTerm, updateDocumentFiltersSearchTerm, updateDocumentFiltersPage]);
return (
<FormProvider {...form}>
<div className="flex gap-2 mb-3">
<ControlledInput<DocumentFiltersSchema>
name="searchTerm"
placeholder="Quick Search"
/>
</div>
</FormProvider>
)
}
export default DocumentFiltersDrawer以上效果,在搜索框发生变动的时候 间隔400ms触发检索,而不是每次变动都触发。
核心代码分析
const searchTerm = useWatch({ control: form.control, name: "searchTerm" });
const debouncedSearchTerm = useDebounce(searchTerm, 400);
useEffect(() => {
updateDocumentFiltersSearchTerm(debouncedSearchTerm);
updateDocumentFiltersPage(1);
}, [debouncedSearchTerm, updateDocumentFiltersSearchTerm, updateDocumentFiltersPage]);
useWatch:监听表单 searchTerm 字段的实时变化
useDebounce:对 searchTerm 做防抖处理
- 每次
searchTerm变化时,设置一个 400ms 的延迟 - 在延迟期间如果
searchTerm再次变化,上一次的定时器会被清掉 - 只有 用户停止输入超过 400ms,
debouncedSearchTerm才会更新
useEffect:监听 debouncedSearchTerm
- 当防抖后的值更新时,执行搜索相关动作(更新搜索条件、重置页码)
防抖逻辑总结
- 用户每输入一个字符 →
searchTerm变化 →useDebounce启动计时器 - 在 400ms 内继续输入 → 上一个计时器清除,重新计时
- 用户停止输入 ≥ 400ms →
debouncedSearchTerm更新 →useEffect执行 - 后续没有输入 → 不再触发,直到下一次输入
时间轴示意图
假设输入 abc,每个字符输入间隔 < 400ms,最后停下来:
时间(ms) → 0 100 200 300 800
输入 a b c (停)
useDebounce 定时器:
─────┐
└───> 被清除
─────┐
└───> 被清除
─────┐
└──> 400ms 后触发 debouncedSearchTerm = "abc"
useEffect 执行:
┌────────────> updateDocumentFiltersSearchTerm("abc")
└────────────> updateDocumentFiltersPage(1)
关键点
- 防抖期间不触发搜索 → 避免频繁请求
- 停顿超过 400ms 才触发一次搜索
- 只触发一次,直到下一次输入