RxJS: How to Render a Real-Time Search Bar

RxJS Search bar
Photo by Ali Hajian on Unsplash

We’ll use RxJS to build a feature-rich search bar that returns real-time results in this post. This will be a nice introduction to RxJS if you’ve never used it before. You’ll see how the RxJS library can simplify a complicated collection of specifications into code that’s easy to read and comprehend.

In a modern application, you’ll almost certainly need to construct a search bar that allows users to search a portion, if not all, of your database.

Any search would involve a request to the server and return the results, which is what we call a real-time search bar or search engine. As we all know, making frequent calls to the server degrades the output of our application. We have no idea how many times our users would be slamming their keys and sending unwanted requests to the server. As a consequence, even something as simple as a search bar needs a variety of protections.

Let’s get started.

Step1: Setup

I’m just using a simple JavaScript project for this example.
There are two references to our key elements, one for the search input and the other for the div element where the results will be loaded.
We use the fromEvent() operator with our search input as the target to transform our input events into an observable series.

Step 2: Minimum Search Term

RxJS has the advantage of allowing one to chain operators together in the same way as array methods can be chained together. Pipe() is a method in Observables that allows us to complete the chain while keeping it simple to read.

Let’s start with the pluck operator and the stream. This helps us to choose the nested property we’re interested in by mapping over the source value (the input’s return object). We’re snagging the value property of the target object, which is nested in the input’s return object, as you can see.

Setting a search term minimum is the next operator, which is visually significant if a huge database needs to be searched. I’m going to set a minimum search term length of 3 characters; anything less than that will not return relevant results. To set the minimum, we’ll use the filter operator, and the length condition will be greater than 2.

Step 3: debounce Time

Let’s start lightening the load on the server. We can use the debounce time operator to ensure that requests are only sent in 500 ms intervals, allowing us to monitor the rate of the user’s input.

Step 4: Distinct Until Change

We now want to reduce the number of API calls that are sent. To achieve this, we want the application to overlook the search term if no changes have occurred since the previous API call. For example, a user could type ‘Medium B,’ then ‘Medium,’ and then ‘Medium,’ and then ‘Medium B,’ and so on. The distinctUntilChange() function is ideal for this since it remembers the previous data passed through the stream and only continues it if it is different.

Step 5: Use the API to make a request.

The code that will query the API must now be added. SwitchMap should be your first preference when dealing with several measurable sources. It’s called the safest flattening default since it moves to a new observable when canceling the previous inner observable, ensuring that responses return in the correct order.

The error and the observable on which the error was caught will be passed to the catchError() operator (in case you wish to retry). Since we don’t want the entire input$ stream to be completed in the event of an error, we capture the error on the ajax observable returned by our switchMap() operator.

Your error-handling code should be placed inside the catchError. In this case, I’m simply logging the error to the console and returning an empty observable, allowing the stream to finish without emitting any values.

Step 6: Enable and Update the Document Object Model

The last move is to update the DOM and trigger it with a subscription. The subscribe operator is used to allow the observer to see data events emitted by the Observable.

Step 7: Optional Add-Ons

It’s worth noting that updating the DOM can be a resource-intensive process, so reducing the number of times you update the DOM can increase your app’s performance.

Depending on your user’s situation and the response from your server, using a filter or distinctUntilChanged operator to minimize the number of DOM updates can be helpful.

We’re not using the distinctUntilChanged operator this time to see if the entire data set has changed (as we did with the input value). Comparing the entire response would result in poor results and a poor user experience, and it would be a very time-consuming process, so instead, we compare the response’s main property.
The data is filtered out and not transmitted down the measurable stream when these two values match (previous response and current response).

Conclusion

That is everything there is to it. This solution offers a powerful, feature-rich, real-time search bar that is optimized for speed and user experience. Since RxJS operators are easy to use and can be chained together, you can easily delete or add operators to the solution above to suit your needs.

If you’re new to RxJS, you’re probably starting to realize just how strong it is and how useful it can be in your next project.

Source Code

Play with code

Do you agree with every single one of the operators I’ve used? Will you use different operators or add more? Please feel free to share your thoughts in the comments section.
Have fun.

I am a passionate software engineer who believes in striving for flawless design on both the client and server side.