Lodash Debounce在React组件中失效:多次触发而非仅执行一次
React防抖失效问题:输入时多次触发函数
当前输入框每输入一个字符,Testrender组件内的函数会在1秒后触发,且连续输入时会多次调用。需求是用户输入结束后仅调用一次,但目前输入过程中就多次触发,同时组件传值时防抖函数无延迟,达不到预期效果。
现有代码
Main_page组件
import { useState } from "react"; import Input from "./Input"; import Testrender from "./Testrender"; const Main_page = () => { const [count, setCount] = useState(""); return ( <> <Input count={count} setCount={setCount} /> <Testrender count={count} /> </> ); }; export default Main_page;
Input组件
import React from "react"; interface Props { count: string; setCount: (searchString: string) => void; } const Input = ({ count, setCount }: Props) => { return ( <div> <input className="test-input" value={count} onChange={(e) => setCount(e.target.value)} placeholder="test render" /> </div> ); }; export default Input;
Testrender组件(原问题代码)
import React, { useCallback, useEffect } from "react"; import { debounce } from "lodash"; interface Props { count: string; } const Testrender = ({ count }: Props) => { const request = debounce((stateValue: any) => { console.log("render"); }, 1000); const changes = useCallback(request, [count]); useEffect(() => { changes(count); }, [count]); return <div>index</div>; }; export default Testrender;
问题原因
- 防抖函数重复创建:每次
count变化导致Testrender重新渲染时,都会重新调用debounce生成新的防抖函数实例。新实例没有之前的计时状态,每次输入都会开启新的1秒延迟,最终多个延迟到期后都会触发,造成多次调用。 - useCallback依赖错误:把
count作为useCallback的依赖,导致每次count变化都生成新的changes函数,相当于每次都是全新的防抖函数,完全失去防抖作用。
修复方案
核心是让防抖函数保持唯一实例,避免每次渲染重新创建。以下是两种可行的修改方式:
方式一:防抖函数定义在组件外部
import React, { useEffect, useRef } from "react"; import { debounce } from "lodash"; interface Props { count: string; } // 防抖函数定义在组件外部,确保全局唯一实例 const debouncedRequest = debounce((stateValue: string) => { console.log("render", stateValue); }, 1000); const Testrender = ({ count }: Props) => { // 用useRef保存防抖函数实例,避免组件内重复创建 const requestRef = useRef(debouncedRequest); useEffect(() => { // 每次count变化时调用防抖函数,连续输入会重置延迟 requestRef.current(count); // 组件卸载时取消未执行的调用,防止内存泄漏 return () => { requestRef.current.cancel(); }; }, [count]); return <div>index</div>; }; export default Testrender;
方式二:用useRef在组件内初始化防抖函数
import React, { useEffect, useRef } from "react"; import { debounce } from "lodash"; interface Props { count: string; } const Testrender = ({ count }: Props) => { // 用useRef存储防抖函数实例,只初始化一次 const requestRef = useRef<ReturnType<typeof debounce>>(); if (!requestRef.current) { requestRef.current = debounce((stateValue: string) => { console.log("render", stateValue); }, 1000); } useEffect(() => { requestRef.current!(count); return () => { requestRef.current!.cancel(); }; }, [count]); return <div>index</div>; }; export default Testrender;
关键说明
- 两种方式都确保了防抖函数只有一个实例,连续输入时会自动重置延迟计时,最终只在输入停止1秒后触发一次。
- 加入了组件卸载时的清理逻辑,调用
cancel()取消未执行的防抖调用,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Виктория
相关产品推荐
相关产品推荐

