I'm trying to achieve an ideal search field that will not make any API calls untill:
I've used a Subject to track for changes in the input field. Every time there's a change in the input field and handleSuggestionsFetchRequested
is called, I'm pushing a new value down the Subject
using searchString$.next(userInput);
And in the useEffect
hook, I'm pipe
ing the searchString$
with debounceTime(350)
and distinctUntilChanged()
. Something like this:
useEffect(() => {
searchString$
.pipe(
debounceTime(350),
distinctUntilChanged(),
switchMap(searchString =>
ajax(`https://api.github.com/search/users?q=${searchString}`)
),
map((networkResponse: any) => networkResponse.response.items)
)
.subscribe((suggestions: Array<User>) => setSuggestions(suggestions));
}, [searchString$]);
But the API calls are still going everytime there's a change in the userInput.
I think the issue is that every time the value of the input field changes, I'm setting the state as well:
const handleChange = (
event: React.ChangeEvent<{}>,
{ newValue }: Autosuggest.ChangeEvent
) => {
setUserInput(newValue);
};
This is causing the Component to re-render and calling the useEffect, which is eventually making the API call again and again.
I could be wrong.
I've created a Sample Code Sandbox that replicates the issue.
Thanks a lot in advance.
Thanks to the comments from yurzui on my tweet, I was able to figure out the reason for the issue.
I was creating a new Subject
on every reconciliation as the line:
const searchString$: Subject<string> = new Subject<string>();
was right inside my component function.
I moved it out and it was working like a charm.
NOTE: As suggested by yurzui, don't forget to catch errors in the ajax call otherwise the Subject
will die.
I've updated the Code Sandbox Sample, just in case somebody needs to refer to it.