我已经使用 React 创建了一个预输入。问题是,如果我输入一些内容并输入退格键,则会显示一些以前的结果。
我明白原因是,由于 API 调用是异步的,它会渲染一些先前的调用结果。那么我们如何才能有效地解决这个问题呢?
codeSandbox:https://codesandbox.io/p/sandbox/typeahead-3xrg9y
import React, { useEffect, useState } from "react";
import { fetchSuggestions } from "./API";
export default function Typeahead() {
const [searchQuery, setSearchQuery] = useState("");
const [results, setResults] = useState([]);
useEffect(() => {
if (!searchQuery) {
setResults([]);
return;
}
getResults(searchQuery);
}, [searchQuery]);
async function getResults(query) {
const result = await fetchSuggestions(query);
if (query === searchQuery) {
setResults(result);
}
}
function inputHandler(e) {
const query = e.target.value;
setSearchQuery(query);
}
return (
<div className="container">
<input onChange={inputHandler} value={searchQuery} />
<div>
<ul className="list-container">
{results.map((res) => (
<li>{res}</li>
))}
</ul>
<div />
</div>
</div>
);
}
您只需要为输入添加一些
debounce
,使用每次输入更改时都会重置的setTimeout
,并且仅在不活动的 300 毫秒(您可以根据需要更改)后调用以获取建议
import React, { useEffect, useState } from "react";
import { fetchSuggestions } from "./API";
export default function Typeahead() {
const [searchQuery, setSearchQuery] = useState("");
const [results, setResults] = useState([]);
const [timerId, setTimerId] = useState(null); // State to keep track of the timeout ID
useEffect(() => {
if (!searchQuery) {
setResults([]);
return;
}
// Clear the existing timeout if there is one
if (timerId) {
clearTimeout(timerId);
}
const newTimerId = setTimeout(() => {
getResults(searchQuery);
}, 300);
setTimerId(newTimerId);
// Cleanup function to clear the timeout if the component is unmounted
return () => clearTimeout(newTimerId);
}, [searchQuery]);
async function getResults(query) {
const result = await fetchSuggestions(query);
if (query === searchQuery) {
setResults(result);
}
}
function inputHandler(e) {
const query = e.target.value;
setSearchQuery(query);
}
return (
<div className="container">
<input onChange={inputHandler} value={searchQuery} />
<div>
<ul className="list-container">
{results.map((res) => (
<li key={res}>{res}</li>
))}
</ul>
<div />
</div>
</div>
);
}