我试图使用debounce在lodash延迟onChange,看到下面的代码。
import React, { useState, useEffect, useCallback } from "react";
import { TopBar } from "@shopify/polaris";
import { debounce } from "lodash";
function SearchBar() {
const [searchValue, setSearchValue] = useState("");
const handleSearchFieldChange = ((value:string) => {
setSearchValue(value);
});
const debounceLoadData = useCallback(debounce({searchValue} => fetchData, 1000), []);
useEffect(() => {
debounceLoadData();
console.log({searchValue})
}, [searchValue]);
function fetchData(value:string) {
console.log("searchValue " + value);
}
const searchFieldMarkup = (
<TopBar.SearchField
onChange={handleSearchFieldChange}
value={searchValue}
placeholder="Search Value"
/>
);
return <TopBar searchField={searchFieldMarkup} />;
}
一开始,我试图使用 searchValue
但似乎由于范围的原因,它无法读取,虽然状态已经更新,但总是空的。
结果,我试着把它从 debounceLoadData
但我不知道如何才能做到这一点,因为useCallback中的内容是一个函数调用。我怎样才能将 searchValue
在 fetchData
里面 debounce
.
lodash debounce将一个函数作为第一个参数。你可以简单地使用 fetchData
作为函数,并传递给 searchValue
到 debounceLoadData
然后将其传递给 fetchData
调用
const debounceLoadData = useCallback(debounce(fetchData, 1000), []);
useEffect(() => {
debounceLoadData(searchValue);
console.log({searchValue})
}, [searchValue]);
debounce实际上是返回一个函数,可以认为debounce的实现方式就像
function debounce(func, wait) {
let timeout
return function(...args) {
const context = this
clearTimeout(timeout)
timeout = setTimeout(() => func.apply(context, args), wait)
}
}
所以基本上 debounceLoadData
是这里的返回函数,而 arguments passed to it i.e ...args
然后被传递给原来的函数fetchData,如 func.apply(context, args)
另外 debounceLoadData
只创建一次,因为回调依赖关系是 []
无论你是否将它作为一个依赖传递给useEffect,都不会有任何区别。
请阅读这篇文章以了解缺少依赖性的警告
我从来不喜欢 useCallback
这是个相当混乱的钩子,我总是用 useMemo
而不是因为它完全涵盖了 useCallback
可以做到(但不是反过来)。
function SearchBar() {
const [searchValue, setSearchValue] = useState("");
const handleSearchFieldChange = ((value:string) => {
setSearchValue(value);
});
const debounceLoadData = useMemo(() => debounce(fetchData, 1000), []);
/**
* the equivalent of useCallback should be:
*
* const debounceLoadData = useCallback(debounce(fetchData, 1000), []);
*
* But I really advice against it!
* There's unnecessary function invocation compared to useMemo.
*/
useEffect(() => {
debounceLoadData(searchValue); // <- you should pass in arg
console.log({searchValue})
}, [searchValue]);
// ...
}
然而对于你的情况,我不认为使用lodashdebounce是最好的解决方案。
有一个隐藏的风险,即最后调用的效果。fetchData
发生在你的组件被卸载之后。而如果 fetchData
包含一些状态突变逻辑,这会引发 "Can't call setState (or forceUpdate) on an unmounted component. "的错误,这不是破坏性的,但也不是最优的。
我建议使用 setTimeout/clearTimeout
. 这是非常简单的。
useEffect(() => {
const timeoutId = setTimeout(() => fetchData(searchValue), 1000)
return () => clearTimeout(timeoutId)
}, [searchValue])
我想你是被功能上的混淆了 setState
语法。试试这个。
const debounceLoadData = useCallback(() => debounce(() => fetchData(searchValue), 1000), []);