This blog post is highly inspired by my recent job experience, since I used to work as an Angular developer for long time and due to a little misunderstanding during a recruitment process I ended up as a React developer which was a little pain for an Angular passionate 😢. Fortunately, only for one month and now I’m back in the Angular game 🎲. One of my tasks was to implement a typeahead/live search component, since we could not use any third-party solutions at all and I had to reinvent the wheel 😄. Since a live search example has always been one of my favourite benchmarks when it comes to Angular/RxJS, I would like to compare the implementation of the task in both Angular and React.
Please note that I will focus on typical solutions for each environment. Of course, one may argue that it’s perfectly fine to use the RxJS library in a React application, but that’s not the goal of the article. It aims at comparison of well-known approaches. So without a further ado, let’s dive into the code! 🏊
Just to make sure that we’re on the same page, I will briefly list the basic requirements for a live serach component:
- a user can type a query into an input element,
- a corresponding list of items matching the query is rendered below the input,
- an http request is made only for a so-called stable query (the required time has to elapse since a user stopped typing),
- an http request should not be performed if a stable query has not changed,
- a race condition has to be handled so that stale results are not rendered.
In my examples I will make use of the JSONPlaceholder REST API which exposes an endpoint for fetching blog posts matching a user id.
Doing it Angular way 🔥
Let’s focus on basic aspects of a solution in Angular. If you’re interested in an in-depth explanation of the RxJS part, I wrote an article about the live search example implemented with the aid of the library which can be found here ⬅️.
Implementing a service
Usually you implement a service which is responsible for performing http requests for a certain domain of your Angular application. That being said, the service which enables fetching user’s posts may look like this:
with the following blog post interface:
For the sake of simplicity, I simply return an empty array for an invalid user id, which is handled by the catchError operator, since the backend service returns a 404 response in such a case.
Implementing a component
Now, you need to implement a component which allows picking a user and renders a list of her/his blog posts. Again, for the sake of simplicity, I have a native input element and a raw list in my example’s template:
Since I do not want to distract your attention from the main topic of the article, I will stick to the simplest solution, namely I will implement a live search logic within the component. However, I highly encourage you to take a look at my previous blog post ⬅️ concerning reusable logic in Angular applications, since you may wish to extract the logic into a dedicated service.
Hence, the component’s class looks like this:
If you are familiar with basic live search/typeahead examples requirements, there is no rocket science in the above code snippet:
- debounceTime — provide only ‘stable’ values,
- distinctUntilChanged — skip the same successive values,
- switchMap — handle race conditions.
Extracting the logic into a custom operator
The logic I have in the component is likely to be reused somewhere else in an Angular application, therefore it’s worth to extract it into a custom RxJS operator. Just to remind you, an operator is simply a function which takes an input stream as its argument and returns an output observable.
Let’s refactor the example using a custom liveSearch operator:
Now, the component class looks cleaner and the live search logic can be easily reused:
Doing it React way 🌈
The first hook allows to trigger re-render (and as a result executes other hooks present in a component) when a given value is ‘stable’, namely a required time has elapsed since the last change:
In the above example, everytime the useDebounce hook is called, the built-in useEffect hook is executed. As a result, if any of the arguments (value or delay) change, the teardown logic is performed (clearing the current setTimeout timer) and the setTimeout function is called with a new callback scheduling the debouncedValue update.
Next step is to define a logic responsible for making an http request for a debounced query only if the current value is different from the previous one:
The useLiveSearch hook makes use of the useDebounce hook in order to receive a ‘stable’ query. If a new debounced value is returned from the hook, the built-in useEffect hook is called. It’s only called when the debouncedQuery changes, since it’s listed in a dependencies array of that hook. Note that it satisfies one of the live search example requirements, namely do not make an http request if a query has not changed. Last but not least, a race condition is handled with the aid of a little trick, namely a response is set only if the isActive flag equals to true. The teardown logic for the useEffect hook toggles the flag which does the job.
This is required for the Promise-based approach, since a Promise object is not cancelable. If you stick to the good old XMLHttpRequest object, you can abort the current request in the body of the teardown logic callback:
You can also use the Axios library which supports a request cancellation.
Since in React there is no official notion of a service, you can stick to a simple factory function which returns an http request:
Implementing a component
Since I’m a big enthusiast of the RxJS library, I’m in favour of the solution typical for Angular applications. The requirements are met with the aid of built-in operators which both save a developer’s time and ensure a high quality of your solution thanks to unit tests. On the other hand, React hooks bring a great value, hence it’s much easier to share logic between components and the new approach is more straightforward than using higher-order components. This blog posts is not meant to be a battle between Angular and React so I will not state who’s the winner, I will simply say that for the last two years I became more mature and so did React 😃.
Below you can find links to the examples:
I hope you liked the post and learned something new 👍 If so, please give me some applause 👏