MST

星途 面试题库

面试题:React中复杂场景下的防抖节流应用

假设你在开发一个React应用,有一个搜索框,用户输入时会触发搜索请求。为了避免频繁请求给服务器造成压力,需要添加防抖功能。同时,在输入框失去焦点时,无论输入是否防抖完成,都要立即发起一次搜索请求。请描述实现思路并给出代码示例,同时说明如何对防抖函数进行测试。
12.9万 热度难度
前端开发React

知识考点

AI 面试

面试题答案

一键面试

实现思路

  1. 防抖函数:通过setTimeout实现防抖逻辑,在设定时间内如果再次触发输入事件,清除之前的定时器并重新设置,确保只有在用户停止输入一段时间后才触发搜索请求。
  2. 失去焦点处理:为输入框添加失去焦点事件监听器,在失去焦点时直接触发搜索函数,而不考虑防抖状态。

代码示例

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;

防抖函数测试

  1. 单元测试框架:可以使用jest进行单元测试。
  2. 测试防抖逻辑
    • 测试在设定时间内多次调用防抖函数,确保只有最后一次调用在延迟后执行。
    • 测试清除定时器逻辑,验证在新的调用时旧的定时器被清除。
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');
    });
});