如何解决异步搜索选项延迟?
How to solve async search options delay?
我正在创建一个 去抖标签搜索表单,它应该获取选项和 return searchResults
来提供 loadOptions
.
问题:由于去抖动,"correct" 收到的选项和显示的选项之间存在一致的延迟。 "correct options" 会在下次调用时显示(最少一个字符)。
想法(可能不是最好的):我想async/awaitloadOptions()
然后等待useSearchTags()
到return.有人在那里 (https://github.com/JedWatson/react-select/issues/3145#issuecomment-434286534) 遇到了同样的问题并分享了解决方案。我的情况有点不同,因为我没有直接获取 loadOptions()
。有什么想法吗?
Codesandbox 最小示例
https://codesandbox.io/s/debounce-react-select-loadoptions-tgud8?file=/src/App.js
捕获
代码
// helpers/useDebouncedSearch.js
import { useState } from 'react';
import AwesomeDebouncePromise from 'awesome-debounce-promise';
import { useAsync } from 'react-async-hook';
import useConstant from 'use-constant';
import to from 'await-to-js';
const useDebouncedSearch = (searchFunction) => {
const [inputText, setInputText] = useState('');
const debouncedSearchFunction = useConstant(() =>
AwesomeDebouncePromise(searchFunction, 300)
);
const searchResults = useAsync(
async () => {
if (inputText.length === 0) {
return [];
} else {
let [err, debouncedResults] = await to(debouncedSearchFunction(inputText));
if(err) return [];
// reformat tags to match AsyncSelect config
const refactorTags = (tags) => {
return tags.map(tag => ({ label: tag.label, value: tag._id }))
}
return (debouncedResults.length !== 0) ?
refactorTags(debouncedResults) :
[];
}
},
[debouncedSearchFunction, inputText]
);
return {
inputText,
setInputText,
searchResults
};
}
export default useDebouncedSearch;
// SearchTags.js
import React, { useRef } from 'react';
import api from '../blablalivre-api.js';
import useDebouncedSearch from '../helpers/useDebouncedSearch.js';
import AsyncCreatableSelect from 'react-select/async-creatable';
import './SearchTags.scss';
const fetchTags = async text =>
(await api.searchTags(text));
const useSearchTags = () => useDebouncedSearch(text => fetchTags(text));
const SearchTagsRender = (props) => {
const { inputText, setInputText, searchResults } = useSearchTags();
const loadOptions = async (inputValue) => {
console.log('searchResults.result: ', searchResults.result);
return searchResults.result;
}
const handleOnChange = (tags) => {
props.updateTagsSelections(tags);
}
// issue AsyncCreatableSelect: https://github.com/JedWatson/react-select/issues/3412
return (
<AsyncCreatableSelect
isCreatable
isMulti
inputValue={inputText}
onInputChange={setInputText}
onChange={handleOnChange}
loadOptions={loadOptions}
cacheOptions
placeholder='Ajouter une thématique'
isClearable={false}
id='search-tags'
classNamePrefix='search-tags'
// to hide menu when input length === 0
openMenuOnClick={false}
// to remove dropdown icon
components={{ DropdownIndicator:() => null, IndicatorSeparator:() => null }}
// to custom display when tag is unknown
formatCreateLabel={inputValue => `Ajouter "${inputValue}"`}
// to reset focus after onChange = needs to user Refs
/>
);
};
export default SearchTagsRender;
非常感谢您的帮助!
皮埃尔
问题在于在您的案例中如何实施 loadOptions。 loadOptions 需要为 AsyncSelect 提供一个承诺,该承诺会在值可用时解决。但是,当您使用 useAsync
提供搜索结果时,它 return 最初会向您提供加载值的响应,然后重新呈现会导致它 return 响应可用时的结果
但是,在您的情况下,loadOptions returns searchResults.result
在加载状态期间是 undefined
。
现在由于 loadOptions
被解析为未定义的值,在下次重新渲染时它不会使用该值,除非输入被更改
这里的解决方案不是不使用 useAsync
而是提供 searchResults
作为 loadOptions 函数
const SearchTagsRender = props => {
const { inputText, setInputText, loaadSearchResults } = useSearchTags();
console.log(loaadSearchResults);
const handleOnChange = tags => {
const tagsFromForm = tags || [];
props.updateTagsFromForm(tagsFromForm);
};
return (
<>
<AsyncCreatableSelect
isCreatable
isMulti
inputValue={inputText}
onInputChange={setInputText}
onChange={handleOnChange}
loadOptions={loaadSearchResults}
cacheOptions
placeholder="Ajouter une thématique"
isClearable={false}
id="search-tags"
classNamePrefix="search-tags"
// to hide menu when input length === 0
openMenuOnClick={false}
// to remove dropdown icon
components={{
DropdownIndicator: () => null,
IndicatorSeparator: () => null
}}
// to custom display when tag is unknown
formatCreateLabel={inputValue => inputValue}
// to reset focus after onChange = needs to user Refs
/>
</>
);
};
export default SearchTagsRender;
const useDebouncedSearch = searchFunction => {
const [inputText, setInputText] = useState("");
const debouncedSearchFunction = useConstant(() =>
AwesomeDebouncePromise(searchFunction, 300)
);
const loaadSearchResults = async () => {
if (inputText.length === 0) {
return [];
} else {
let [err, debouncedResults] = await to(
debouncedSearchFunction(inputText)
);
if (err) return [];
console.log("debouncedResults: ", debouncedResults);
// reformat tags to match AsyncSelect config
const refactorItems = items => {
return items.map(item => ({
label: item.name,
value: item.alpha3Code
}));
};
return debouncedResults.length !== 0
? refactorItems(debouncedResults)
: [];
}
};
return {
inputText,
setInputText,
loaadSearchResults
};
};
我正在创建一个 去抖标签搜索表单,它应该获取选项和 return searchResults
来提供 loadOptions
.
问题:由于去抖动,"correct" 收到的选项和显示的选项之间存在一致的延迟。 "correct options" 会在下次调用时显示(最少一个字符)。
想法(可能不是最好的):我想async/awaitloadOptions()
然后等待useSearchTags()
到return.有人在那里 (https://github.com/JedWatson/react-select/issues/3145#issuecomment-434286534) 遇到了同样的问题并分享了解决方案。我的情况有点不同,因为我没有直接获取 loadOptions()
。有什么想法吗?
Codesandbox 最小示例
https://codesandbox.io/s/debounce-react-select-loadoptions-tgud8?file=/src/App.js
捕获
代码
// helpers/useDebouncedSearch.js
import { useState } from 'react';
import AwesomeDebouncePromise from 'awesome-debounce-promise';
import { useAsync } from 'react-async-hook';
import useConstant from 'use-constant';
import to from 'await-to-js';
const useDebouncedSearch = (searchFunction) => {
const [inputText, setInputText] = useState('');
const debouncedSearchFunction = useConstant(() =>
AwesomeDebouncePromise(searchFunction, 300)
);
const searchResults = useAsync(
async () => {
if (inputText.length === 0) {
return [];
} else {
let [err, debouncedResults] = await to(debouncedSearchFunction(inputText));
if(err) return [];
// reformat tags to match AsyncSelect config
const refactorTags = (tags) => {
return tags.map(tag => ({ label: tag.label, value: tag._id }))
}
return (debouncedResults.length !== 0) ?
refactorTags(debouncedResults) :
[];
}
},
[debouncedSearchFunction, inputText]
);
return {
inputText,
setInputText,
searchResults
};
}
export default useDebouncedSearch;
// SearchTags.js
import React, { useRef } from 'react';
import api from '../blablalivre-api.js';
import useDebouncedSearch from '../helpers/useDebouncedSearch.js';
import AsyncCreatableSelect from 'react-select/async-creatable';
import './SearchTags.scss';
const fetchTags = async text =>
(await api.searchTags(text));
const useSearchTags = () => useDebouncedSearch(text => fetchTags(text));
const SearchTagsRender = (props) => {
const { inputText, setInputText, searchResults } = useSearchTags();
const loadOptions = async (inputValue) => {
console.log('searchResults.result: ', searchResults.result);
return searchResults.result;
}
const handleOnChange = (tags) => {
props.updateTagsSelections(tags);
}
// issue AsyncCreatableSelect: https://github.com/JedWatson/react-select/issues/3412
return (
<AsyncCreatableSelect
isCreatable
isMulti
inputValue={inputText}
onInputChange={setInputText}
onChange={handleOnChange}
loadOptions={loadOptions}
cacheOptions
placeholder='Ajouter une thématique'
isClearable={false}
id='search-tags'
classNamePrefix='search-tags'
// to hide menu when input length === 0
openMenuOnClick={false}
// to remove dropdown icon
components={{ DropdownIndicator:() => null, IndicatorSeparator:() => null }}
// to custom display when tag is unknown
formatCreateLabel={inputValue => `Ajouter "${inputValue}"`}
// to reset focus after onChange = needs to user Refs
/>
);
};
export default SearchTagsRender;
非常感谢您的帮助!
皮埃尔
问题在于在您的案例中如何实施 loadOptions。 loadOptions 需要为 AsyncSelect 提供一个承诺,该承诺会在值可用时解决。但是,当您使用 useAsync
提供搜索结果时,它 return 最初会向您提供加载值的响应,然后重新呈现会导致它 return 响应可用时的结果
但是,在您的情况下,loadOptions returns searchResults.result
在加载状态期间是 undefined
。
现在由于 loadOptions
被解析为未定义的值,在下次重新渲染时它不会使用该值,除非输入被更改
这里的解决方案不是不使用 useAsync
而是提供 searchResults
作为 loadOptions 函数
const SearchTagsRender = props => {
const { inputText, setInputText, loaadSearchResults } = useSearchTags();
console.log(loaadSearchResults);
const handleOnChange = tags => {
const tagsFromForm = tags || [];
props.updateTagsFromForm(tagsFromForm);
};
return (
<>
<AsyncCreatableSelect
isCreatable
isMulti
inputValue={inputText}
onInputChange={setInputText}
onChange={handleOnChange}
loadOptions={loaadSearchResults}
cacheOptions
placeholder="Ajouter une thématique"
isClearable={false}
id="search-tags"
classNamePrefix="search-tags"
// to hide menu when input length === 0
openMenuOnClick={false}
// to remove dropdown icon
components={{
DropdownIndicator: () => null,
IndicatorSeparator: () => null
}}
// to custom display when tag is unknown
formatCreateLabel={inputValue => inputValue}
// to reset focus after onChange = needs to user Refs
/>
</>
);
};
export default SearchTagsRender;
const useDebouncedSearch = searchFunction => {
const [inputText, setInputText] = useState("");
const debouncedSearchFunction = useConstant(() =>
AwesomeDebouncePromise(searchFunction, 300)
);
const loaadSearchResults = async () => {
if (inputText.length === 0) {
return [];
} else {
let [err, debouncedResults] = await to(
debouncedSearchFunction(inputText)
);
if (err) return [];
console.log("debouncedResults: ", debouncedResults);
// reformat tags to match AsyncSelect config
const refactorItems = items => {
return items.map(item => ({
label: item.name,
value: item.alpha3Code
}));
};
return debouncedResults.length !== 0
? refactorItems(debouncedResults)
: [];
}
};
return {
inputText,
setInputText,
loaadSearchResults
};
};