我正在创建一个去抖动的标签搜索表单,该表单应获取选项并返回searchResults
以供以loadOptions
。
问题:由于存在反跳,因此“正确”的接收选项和显示的选项之间存在一致的延迟。下次呼叫时显示“正确的选项”(最少一个字符)。
想法(可能不是最好的):我想异步/等待loadOptions()
并等待useSearchTags()
返回。那里有人遇到相同的问题(https://github.com/JedWatson/react-select/issues/3145#issuecomment-434286534),并分享了一个解决方案。我的情况有些不同,因为我没有直接在loadOptions()
中获取。有什么主意吗?
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提供一个Promise,该Promise会在值可用时进行解析。但是,当您使用useAsync
提供searchResults时。它首先返回一个带有加载值的响应,然后在响应可用时进行重新渲染,使其返回结果。
但是,在您的情况下,loadOptions从loadOptions返回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 }; };