在这篇文章中,我将使用React Hooks API来实现debouncing。如果你对debouncing不熟悉,它只是一种机制,用于推迟一个函数的执行。通常情况下,这将在javascript中实现,以防止一个潜在的昂贵的操作(例如,从后端请求数据)被过早执行。现实生活中的一个例子是一个typeahead列表,它只在用户停止输入后的一段时间内执行搜索。
设置
我将使用一个例子,我们根据用户的输入来过滤美国各州的JSON文件。我们不希望在用户停止输入之前过滤列表,所以我们要实现跳转。我们的JSON文件将采用以下格式。
[
{
"name": "Alabama",
"abbreviation": "AL"
},
{
"name": "Alaska",
"abbreviation": "AK"
},
...
]
为了简单起见,我们将只根据用户的输入来过滤name 。
实现无跳转的过滤钩子
让我们快速实现没有去抖的功能。这意味着在用户输入时,状态列表将被自动过滤。
import React, { useState, useEffect, Fragment } from 'react';
import states from './states';
const App = () => {
const [search, setSearch] = useState('');
const [filteredStates, setFilteredStates] = useState(states);
useEffect(() => {
const filter = states.filter((state) => {
return state.name.toLowerCase().includes(search.toLowerCase());
});
setFilteredStates(filter);
}, [search]);
return (
<Fragment>
<input value={search} onChange={(e) => setSearch(e.target.value)} />
<ul>
{filteredStates &&
filteredStates.map((state) => {
return <li key={state.abbreviation}>{state.name}</li>;
})}
</ul>
</Fragment>
);
};
export default App;
当我们的输入元素发生变化时,setSearch 被调用,这将改变我们的search 值,重新渲染组件。useEffect 钩子就像我们所指出的那样,只要search 发生变化,它就会运行。在useEffect 钩子中,我们根据search 的值来过滤states 。
如果我们在这时运行我们的应用程序,我们会看到它的功能符合要求。
添加去抖动
为了增加调试,我们可以直觉地认为,useEffect 钩子应该用一个定时器来更新。让我们添加一个有1秒等待时间的setTimeout ,看看效果如何。
useEffect(() => {
setTimeout(() => {
const filter = states.filter((state) => {
return state.name.toLowerCase().includes(search.toLowerCase());
});
setFilteredStates(filter);
}, 1000);
}, [search]);
这并不完全正确。它确实延迟了过滤,但它并没有等到你停止输入。基本上,应用程序现在只是等待一秒钟,然后开始追赶你正在输入的内容。这不是理想的行为--完全相同的过滤量正在发生。
有一个简单的解决方法:useEffect 钩子提供了一个 "清理 "机制,当组件下一次重新显示时将会运行。我们所要做的就是从传递给useEffect 钩子的第一个函数中返回清理函数。在我们的例子中,我们只需要取消setTimeout 。如果在用户输入更多信息之前,1秒还没有过去,setTimeout 将被取消,过滤器将不会被应用。
useEffect(() => {
const timer = setTimeout(() => {
const filter = states.filter((state) => {
return state.name.toLowerCase().includes(search.toLowerCase());
});
setFilteredStates(filter);
}, 1000);
return () => clearTimeout(timer);
}, [search]);
成功了!我们现在要等到用户最后一次输入后的1秒来过滤这些状态。完整的代码可以在下面查看。
App.js
import React, { useState, useEffect, Fragment } from 'react';
import states from './states';
const App = () => {
console.log('render');
const [search, setSearch] = useState('');
const [filteredStates, setFilteredStates] = useState(states);
useEffect(() => {
const timer = setTimeout(() => {
const filter = states.filter((state) => {
return state.name.toLowerCase().includes(search.toLowerCase());
});
setFilteredStates(filter);
}, 1000);
return () => clearTimeout(timer);
}, [search]);
return (
<Fragment>
<input value={search} onChange={(e) => setSearch(e.target.value)} />
<ul>
{filteredStates &&
filteredStates.map((state) => {
return <li key={state.abbreviation}>{state.name}</li>;
})}
</ul>
</Fragment>
);
};
export default App;
states.json
[
{
"name": "Alabama",
"abbreviation": "AL"
},
{
"name": "Alaska",
"abbreviation": "AK"
},
{
"name": "American Samoa",
"abbreviation": "AS"
},
{
"name": "Arizona",
"abbreviation": "AZ"
},
{
"name": "Arkansas",
"abbreviation": "AR"
},
{
"name": "California",
"abbreviation": "CA"
},
{
"name": "Colorado",
"abbreviation": "CO"
},
{
"name": "Connecticut",
"abbreviation": "CT"
},
{
"name": "Delaware",
"abbreviation": "DE"
},
{
"name": "District Of Columbia",
"abbreviation": "DC"
},
{
"name": "Federated States Of Micronesia",
"abbreviation": "FM"
},
{
"name": "Florida",
"abbreviation": "FL"
},
{
"name": "Georgia",
"abbreviation": "GA"
},
{
"name": "Guam",
"abbreviation": "GU"
},
{
"name": "Hawaii",
"abbreviation": "HI"
},
{
"name": "Idaho",
"abbreviation": "ID"
},
{
"name": "Illinois",
"abbreviation": "IL"
},
{
"name": "Indiana",
"abbreviation": "IN"
},
{
"name": "Iowa",
"abbreviation": "IA"
},
{
"name": "Kansas",
"abbreviation": "KS"
},
{
"name": "Kentucky",
"abbreviation": "KY"
},
{
"name": "Louisiana",
"abbreviation": "LA"
},
{
"name": "Maine",
"abbreviation": "ME"
},
{
"name": "Marshall Islands",
"abbreviation": "MH"
},
{
"name": "Maryland",
"abbreviation": "MD"
},
{
"name": "Massachusetts",
"abbreviation": "MA"
},
{
"name": "Michigan",
"abbreviation": "MI"
},
{
"name": "Minnesota",
"abbreviation": "MN"
},
{
"name": "Mississippi",
"abbreviation": "MS"
},
{
"name": "Missouri",
"abbreviation": "MO"
},
{
"name": "Montana",
"abbreviation": "MT"
},
{
"name": "Nebraska",
"abbreviation": "NE"
},
{
"name": "Nevada",
"abbreviation": "NV"
},
{
"name": "New Hampshire",
"abbreviation": "NH"
},
{
"name": "New Jersey",
"abbreviation": "NJ"
},
{
"name": "New Mexico",
"abbreviation": "NM"
},
{
"name": "New York",
"abbreviation": "NY"
},
{
"name": "North Carolina",
"abbreviation": "NC"
},
{
"name": "North Dakota",
"abbreviation": "ND"
},
{
"name": "Northern Mariana Islands",
"abbreviation": "MP"
},
{
"name": "Ohio",
"abbreviation": "OH"
},
{
"name": "Oklahoma",
"abbreviation": "OK"
},
{
"name": "Oregon",
"abbreviation": "OR"
},
{
"name": "Palau",
"abbreviation": "PW"
},
{
"name": "Pennsylvania",
"abbreviation": "PA"
},
{
"name": "Puerto Rico",
"abbreviation": "PR"
},
{
"name": "Rhode Island",
"abbreviation": "RI"
},
{
"name": "South Carolina",
"abbreviation": "SC"
},
{
"name": "South Dakota",
"abbreviation": "SD"
},
{
"name": "Tennessee",
"abbreviation": "TN"
},
{
"name": "Texas",
"abbreviation": "TX"
},
{
"name": "Utah",
"abbreviation": "UT"
},
{
"name": "Vermont",
"abbreviation": "VT"
},
{
"name": "Virgin Islands",
"abbreviation": "VI"
},
{
"name": "Virginia",
"abbreviation": "VA"
},
{
"name": "Washington",
"abbreviation": "WA"
},
{
"name": "West Virginia",
"abbreviation": "WV"
},
{
"name": "Wisconsin",
"abbreviation": "WI"
},
{
"name": "Wyoming",
"abbreviation": "WY"
}
]