发布于 2026-01-06 0 阅读
0

React 自动完成搜索输入(防抖)

React 自动完成搜索输入(防抖)

另一个备受期待的功能是:
根据用户在输入框中输入的内容,我们必须显示数据库中的建议。

它通常用于电子商务,因为它可以改善用户体验并加快购买速度。

在我们的示例中,我们将使用API作为后端。

这样就可以根据参数找到要使用的公共 API。
接下来,我们将创建一个输入框,根据用户输入的内容,向他显示与搜索词匹配的公共 API 列表。
最终项目

此组件需要以下库:
yarn add axios(用于发起 API 请求)、
yarn add styled-components *(用于使用 JavaScript 创建 CSS,顺便说一下,您可以在普通的 Sass 文件中实现代码)、
yarn add lodash.debounce(稍后我们将详细介绍)。


我们先从创建 requests.js 文件开始
。这个文件将负责向 API 发送请求。

const url = axios.create({
    baseURL: 'https://api.publicapis.org/',
});

export const getApiSuggestions = (word) => {
    let result = url
        .get(`/entries?title=${word}`)
        .then((response) => {
            return response.data;
        })
        .catch((error) => {
            return error;
        });

    return result;
};
Enter fullscreen mode Exit fullscreen mode

现在我们来创建 searchInput 组件,首先我们需要一些样式,这要借助 styled-components 来实现。

import styled from 'styled-components';

export const Input = styled.input`
    width: 222px;
    height: 51px;
    padding: 10px;
    background: #f3f3f3;
    box-shadow: inset 0px 4px 4px rgba(0, 0, 0, 0.1);
    border-radius: 5px;
    border: none;
`;

export const Ul = styled.ul`
    display: contents;
`;

export const Li = styled.ul`
    width: 222px;
    font-weight: bold;
    height: 51px;
    padding: 10px;
    background: #f5f0f0;
    display: block;
    border-bottom: 1px solid white;
    &:hover {
        cursor: pointer;
        background-color: rgba(0, 0, 0, 0.14);
    }
`;

export const SuggestContainer = styled.div`
    height: 240px;
    width: 242px;
    overflow: scroll;
    &::-webkit-scrollbar {
        display: none;
    }
    -ms-overflow-style: none; /* IE and Edge */
    scrollbar-width: none; /* Firefox */
`;
Enter fullscreen mode Exit fullscreen mode

现在我们的组件

import React, { useState, useCallback } from 'react';

import { Input, Ul, Li, SuggestContainer } from './style';

export default function SearchInput({
    loading,
    options,
    requests,
    placeholder,
}) {
    const [inputValue, setInputValue] = useState('');

    const updateValue = (newValue) => {
        setInputValue(newValue);
        requests(newValue);
    };

    return (
        <div>
            <Input
                value={inputValue}
                onChange={(input) => updateValue(input.target.value)}
                placeholder={placeholder}
            />
            <SuggestContainer>
                <Ul>
                    {loading && <Li>Loading...</Li>}
                    {options?.entries?.length > 0 &&
                        !loading &&
                        options?.entries?.map((value, index) => (
                            <Li key={`${value.API}-${index}`}>{value.API}</Li>
                        ))}
                </Ul>
            </SuggestContainer>
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

现在让我们来了解一下这些参数:

加载状态:此状态从父组件传递而来,用于在发出相应请求时显示加载消息。
选项:这是我们想要显示为建议的对象数组。
请求:这是我们将要执行搜索的请求,父组件包含该函数,但由此组件执行。

函数:
updateValue:我们主要处理受控组件,此函数负责设置新的输入值,并将该值发送到我们的请求。

渲染代码中最重要的部分:

渲染代码

首先,我们验证 loading 是否为真,如果是,则仅显示 loading 值,直到请求完成。
我们的第二个验证确保 loading 为假,并且我们的选项数组包含一些要显示的值,否则将被忽略。

`.?` 是一个可选的更改,允许读取位于连接对象链中的属性值,而无需显式验证链中每个引用是否有效。
换句话说,它可以避免在 `entries` 属性不存在时数组不存在,或者映射一个空对象的情况。

让我们来创建我们的应用程序

import React, { useState, useEffect } from 'react';
import { getApiSuggestions } from './requests';
import SearchInput from './searchInput';
import { MainWrapper } from './style';

function App() {
    const [options, setOptions] = useState([]);
    const [loading, setLoading] = useState(false);

    const getSuggestions = async (word) => {
        if (word) {
            setLoading(true);
            let response = await getApiSuggestions(word);
            setOptions(response);
            setLoading(false);
        } else {
            setOptions([]);
        }
    };

    const getApiUrl = (url) => {
        window.open(url, '_blank');
    };

    return (
        <MainWrapper>
            <SearchInput
                loading={loading}
                options={options}
                requests={getSuggestions}
                onClickFunction={getApiUrl}
                placeholder="find a public api"
            />
        </MainWrapper>
    );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

功能:

`getSuggestions`:这是我们要传递给组件的函数,它首先验证是否存在待搜索的值(我们不会发送空值,否则请求将毫无意义)。
如果不存在,我们会清空选项对象,以便在搜索词为空时不显示建议。
之后,利用 `async/await`,我们等待请求完成并返回值,然后将其设置到选项中,也就是我们要传递给组件的状态。`getApiUrl`
:我们将把这个函数传递给组件,它会在新标签页中打开一个 URL。

综上所述,我们的组件应该按如下方式工作。

第一个版本应用程序

它现在能用了,但你看出问题了吗?
每个字母我们都要向 API 发送一个请求。
很危险。想象一下,如果有 1 万个用户使用你的项目,每个用户完成一次搜索最终都要向 API 发送 2 万个请求,这既不可持续,也是糟糕的做法。

那么我们该如何解决这个问题呢?去抖动

什么是防抖?
它是一个返回一个函数的函数,该函数可以被调用任意次数(可能快速连续调用),但只有在上次调用后等待 x 毫秒才会调用回调函数。

让我们重建搜索输入

import React, { useState, useCallback } from 'react';
import debounce from 'lodash.debounce';

import { Input, Ul, Li, SuggestContainer } from './style';

export default function SearchInput({
    loading,
    options,
    requests,
    onClickFunction,
    placeholder,
}) {
    const [inputValue, setInputValue] = useState('');

    const debouncedSave = useCallback(
        debounce((newValue) => requests(newValue), 1000),
        []
    );

    const updateValue = (newValue) => {
        setInputValue(newValue);
        debouncedSave(newValue);
    };

    return (
        <div>
            <Input
                value={inputValue}
                onChange={(input) => updateValue(input.target.value)}
                placeholder={placeholder}
            />
            <SuggestContainer>
                <Ul>
                    {loading && <Li>Loading...</Li>}
                    {options?.entries?.length > 0 &&
                        !loading &&
                        options?.entries?.map((value, index) => (
                            <Li
                                key={`${value.API}-${index}`}
                                onClick={() => onClickFunction(value.Link)}
                            >
                                {value.API}
                            </Li>
                        ))}
                </Ul>
            </SuggestContainer>
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

功能:

debouncedSave:
首先使用 useCallback,传入一个在线回调函数和一个依赖项数组。useCallback 会返回一个记忆化的回调函数版本,只有当某个依赖项发生变化时,该版本才会更新。
然后,我们利用 lodash.debounce 中的 debounce 功能,告诉它这个函数会在特定时间后执行。
这样,我们就允许请求在特定时间后才执行,从而让用户能够编写真正的搜索语句,而不是疯狂地发送查询。

让我们看看实际应用中的变化是如何发生的。
最终项目

找到了!现在有了防抖功能,我们的函数只会在经过一段时间后才执行请求,这样我们就给了用户时间输入有效的搜索词。

我们避免了用垃圾请求填充我们的 API,并且我们改善了用户体验。

需要改进的地方:
这个 API 没有限制,更合适的做法是将响应数量限制为 3-5 条,因为显示 50 条建议并非最佳方案。3-5 条建议最为理想。

完整代码

文章来源:https://dev.to/danilo95/react-autocomplete-search-input-debounce-2mof