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;
};
现在我们来创建 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 */
`;
现在我们的组件
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>
);
}
现在让我们来了解一下这些参数:
加载状态:此状态从父组件传递而来,用于在发出相应请求时显示加载消息。
选项:这是我们想要显示为建议的对象数组。
请求:这是我们将要执行搜索的请求,父组件包含该函数,但由此组件执行。
函数:
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;
功能:
`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>
);
}
功能:
debouncedSave:
首先使用 useCallback,传入一个在线回调函数和一个依赖项数组。useCallback 会返回一个记忆化的回调函数版本,只有当某个依赖项发生变化时,该版本才会更新。
然后,我们利用 lodash.debounce 中的 debounce 功能,告诉它这个函数会在特定时间后执行。
这样,我们就允许请求在特定时间后才执行,从而让用户能够编写真正的搜索语句,而不是疯狂地发送查询。
找到了!现在有了防抖功能,我们的函数只会在经过一段时间后才执行请求,这样我们就给了用户时间输入有效的搜索词。
我们避免了用垃圾请求填充我们的 API,并且我们改善了用户体验。
需要改进的地方:
这个 API 没有限制,更合适的做法是将响应数量限制为 3-5 条,因为显示 50 条建议并非最佳方案。3-5 条建议最为理想。


