实现思路
- 防抖函数:通过
setTimeout
实现防抖逻辑,在设定时间内如果再次触发输入事件,清除之前的定时器并重新设置,确保只有在用户停止输入一段时间后才触发搜索请求。
- 失去焦点处理:为输入框添加失去焦点事件监听器,在失去焦点时直接触发搜索函数,而不考虑防抖状态。
代码示例
import React, { useState, useRef } from'react';
// 防抖函数
const debounce = (func, delay) => {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
};
const SearchComponent = () => {
const [searchTerm, setSearchTerm] = useState('');
const searchRef = useRef();
const handleSearch = (term) => {
// 实际搜索逻辑,这里简单打印
console.log('Searching for:', term);
};
const debouncedSearch = debounce(handleSearch, 500);
const handleInputChange = (e) => {
setSearchTerm(e.target.value);
debouncedSearch(e.target.value);
};
const handleBlur = () => {
handleSearch(searchTerm);
};
return (
<div>
<input
type="text"
value={searchTerm}
onChange={handleInputChange}
onBlur={handleBlur}
ref={searchRef}
/>
</div>
);
};
export default SearchComponent;
防抖函数测试
- 单元测试框架:可以使用
jest
进行单元测试。
- 测试防抖逻辑:
- 测试在设定时间内多次调用防抖函数,确保只有最后一次调用在延迟后执行。
- 测试清除定时器逻辑,验证在新的调用时旧的定时器被清除。
import React from'react';
import { render, fireEvent } from '@testing-library/react';
import SearchComponent from './SearchComponent';
// 测试防抖函数
jest.useFakeTimers();
describe('SearchComponent', () => {
it('should call handleSearch after debounce delay', () => {
const { getByPlaceholderText } = render(<SearchComponent />);
const input = getByPlaceholderText('Search...');
const handleSearchMock = jest.fn();
const debouncedSearch = debounce(handleSearchMock, 500);
fireEvent.change(input, { target: { value: 'test' } });
expect(handleSearchMock).not.toHaveBeenCalled();
jest.advanceTimersByTime(500);
expect(handleSearchMock).toHaveBeenCalledWith('test');
});
it('should clear timer on new input', () => {
const { getByPlaceholderText } = render(<SearchComponent />);
const input = getByPlaceholderText('Search...');
const handleSearchMock = jest.fn();
const debouncedSearch = debounce(handleSearchMock, 500);
fireEvent.change(input, { target: { value: 'test1' } });
fireEvent.change(input, { target: { value: 'test2' } });
expect(handleSearchMock).not.toHaveBeenCalled();
jest.advanceTimersByTime(500);
expect(handleSearchMock).toHaveBeenCalledWith('test2');
});
});