React中useRef与useMemo实现防抖:两种写法差异解析
React防抖实现中两种写法的差异解析
先看你的代码实现:
import { useEffect, useRef, useState, useMemo } from "react"; function debounce(fn) { let timer = null; return function () { clearTimeout(timer); timer = setTimeout(fn, 1000) } } export default function () { const [value, setValue] = useState(""); const myRef = useRef(handlChangeUtil); //打印state的值 //闭包:该函数捕获了'value' function handlChangeUtil() { console.log(value); } //将handlChangeUtil的最新版本绑定到myRef //因为首次挂载的函数无法获取更新后的value值 useEffect(() => { myRef.current = handlChangeUtil; }, [value]); //仅在组件挂载时返回防抖函数 const handlChangeUtilDebounce = useMemo(() => { //这段代码可以正常运行 // const func = () => { // myRef.current(); // } // return debounce(func) //这段代码无法正常运行 return debounce(myRef.current); } , []); function handleChange(e) { setValue(e.target.value); handlChangeUtilDebounce(); } return ( <div> <input value={value} onChange={handleChange} /> </div> ) }
两种写法的核心差异在于debounce函数捕获的是哪个函数:
1. 直接传入myRef.current的写法(无法正常运行)
因为useMemo的依赖是空数组,所以只会在组件首次挂载时执行一次。此时myRef.current是组件第一次渲染时生成的handlChangeUtil函数——这个函数通过闭包捕获了初始的value(空字符串)。
之后虽然useEffect会在value变化时把myRef.current更新为新的handlChangeUtil(每次value变化都会生成新的函数,捕获当前最新的value),但debounce内部保存的fn还是首次传入的那个旧函数。当防抖触发时,执行的是旧的handlChangeUtil,自然只会打印初始的空字符串。
2. 包裹一层匿名函数的写法(正常运行)
这里传给debounce的是一个匿名函数() => { myRef.current(); },useMemo同样只在首次挂载时把这个匿名函数传给debounce。但这个匿名函数本身不会捕获固定的value,它只有在执行时才会去读取myRef.current的最新值——也就是useEffect更新后的、捕获了当前最新value的handlChangeUtil函数。所以每次防抖触发时,执行的都是最新的handlChangeUtil,能正确打印更新后的value。
简单来说,第一种写法让debounce记住了旧的函数,第二种写法让debounce记住的是一个“动态取最新函数”的逻辑。
内容的提问来源于stack exchange,提问作者Sarthak Gupta
相关产品推荐
相关产品推荐

