I do understand that this problem is very common and most people might find this as a duplicate but I am at my wits end and that is why I am here.
I have a React component called App
is a functional component.
Start of App
component
function App() {
const [results, setResults] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [name, setName] = useState('');
const [isNameSelected, setIsNameSelected] = useState(false);
...
There is a child component which is acting erratically at the moment and it is part of the return components of the App
function.
Section of Code in return
statement of the App
component:
<ListGroup className="typeahead-list-group">
{!isNameSelected &&
results.length > 0 &&
results.map((result: Result) => (
<ListGroupItem
key={result.cik}
className="typeahead-list-group-item"
onClick={() => onNameSelected(result)}
>
{result.cik ' | ' result.name}
</ListGroupItem>
))}
</ListGroup>
A change to to results
is handled by the component FormControl
's onChange
function here also part of the return
statement of App
:
<FormControl
placeholder="Entity/CIK"
id="searchInput"
type="text"
autoComplete="off"
onChange={handleInputChange}
value={name}
/>
handleInputChange
is defined in App
function as:
const handleInputChange = (e: any) => { // Triggers when search bar is changed
// remove error bubble
setAlertMessage(new AlertData('', false));
const nameValue = e.target.value; // get the new input
setName(nameValue); // set the new input
// even if we've selected already an item from the list, we should reset it since it's been changed
setIsNameSelected(false);
setResults([]); // clean previous results
if (nameValue.length > 1) { // if the input is more than 1 character
setIsLoading(true); // set loading to true
updateSearchInput(nameValue) // get the results
.then((res) => {
setResults(res as React.SetStateAction<never[]>); // set the results
setIsLoading(false); // set loading to false
})
.catch((error) => {
// error bubble
let strError = error.message;
strError = strError.split(':').pop();
let errorMessage: AlertData = new AlertData(strError, true); // create error message for empty search
setAlertMessage(errorMessage); // set alert message
// loading spinner
setIsLoading(false);
});
}
}
However when there is an input change in form control, like typing in an entire word, the search functionality works, populating the DOM with suggested words. However when I clear the value in FormControl
really fast (by pressing Backspace/Delete several times in quick succession), then the search results stay. Doing it slow or selecting and clearing it all at once however does not show this erratic behavior.
I have used console.log
to print out the value of results
in the an empty component like this:
{console.log(results) && (<div><div/>)}
in return
statement of App to see what the contents of results
are. However it does show that results
value were not updated by setResults()
.
This problem however does not exist for the other states utilized here. Why?
EDIT
From the answer accepted below from @ghybs. This is a timeline of what might be happening with the call:
- Enter search
await
call runs but request response is slow so takes a while.- Delete all the keyword in search
results
is made empty withsetResults([])
inhandleInputChange
call.- await call finishes.
setResults(res as React.SetStateAction<never[]>)
runs makingresults
non-empty.
CodePudding user response:
You very probably just have plenty concurrent requests (1 per key stroke, including back space?), and unordered results from your updateSearchInput
async function: the last received one overwrites your results
, but that one may not originate from your last key stroke (the one that removed the last character from your textarea).
Typically if an empty search is faster than a search with plenty words, the results from empty input do clear your results
, but then these are filled again by the results of a previous search.