TOP 6 Mistakes in RxJS code

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Hi welcome back. In this video I'm going to  share with you my personal rating of the top   6 mistakes that developers do in RxJS code. This  video is focused more for beginners with Angular,   but I think even more experienced developers can  learn something new from it. If it is your first   time on my channel, my name is Dmytro Mezhenskyi and my goal is  to help you to become Advanced Angular developer. Who   develops Angular applications consciously knowing  what you're doing and why. In the past I created   quite a lot of advanced and unique tutorials  about Angular of course and you can check them   out later but now let's focus on RxJS and the  mistakes that we, time after time, see in our code base. All right for this tutorial I prepared a  demo component the code of which is actually full   of bad practices and mistakes, but before we start  to explore and fix them all let me very quickly   introduce an application and provide more context  about its implementation. So the application mostly   consists of one single component the user search  component which allows me to find users by typing   their names eventually the users I find will be  displayed there below. We can also define how many   results I want to see per page and I can do that by selecting the corresponding value from the   dropdown. Okay what about the implementation  the implementation is indeed very simple in   the template I have a small reactive form that  contains the text input and the select element and   the value of this form is going to be the search  configuration that will be used to configure the   HTTP call in the code it looks like the following  you can see that I created an observable searchConfig    starting from the form value changes so  when the user changes any of these form controls   the new config object is emitted. Then I subscribe  to the searchConfig observable in order to get   config object and provide this object to the  findUsers() method as an argument which performs   under the hood the HTTP call to the server with  the parameters defined in the searchConfig then   I subscribe to the observable that this method  returns in order to kick off the HTTP call once   data is received I get it inside the subscribe  callback and assign it to the class property so   that I can use this data in the component template  to display their corresponding information. So this   is how it is currently implemented, implemented  terribly implemented wrongly, but this is what   I see very often reviewing the projects of my  clients now let's see what is wrong with this code .  And the mistake number one is nested subscriptions  you know when it comes to the software development   I'm a kind of person who tries to avoid such a  strong words like never or always, but this is one   of those rare cases where I can undoubtedly say  please never do this. Despite it looking ugly and   messy by subscribing to the high order observable  you lose their reactive context for a while, then   you probably perform some imperative logic in  between and again you enter the reactive context   in the nested observable and this opens a lot of  space for different kinds of mistakes and errors   and this is not how you are supposed to work with RxJs. In RxJs you should try to keep your data   within the reactive context as long as possible  and resolve data at the very last moment. So how   could we make this part better, in scenarios like  this you should always use the RxJs flattening   operators. The most popular one is the switchMap() operator which you can apply in the pipe() method of the high order observable. This operator  requires a callback function the argument of which   one has a value emitted by the high order  observable. The return value of the callback   has to be en nested observable which is in my  case the one that defined users method returns. The switchMap() will under the hood, subscribe  to this nested observable kicking off the HTTP   call. And the data returned from the server  will go to the subscribe callback of the   high order observable. So now I just have to  clean up my code a little right here and I will get the same result actually as I had  before, but now it is cleaner, it is safer and   more predictable, and another cool thing that the  switchMap() operator does is that if a new search   config arrives while the current HTTP call is  pending, the switchMap() will cancel the pending   call and schedule the new one with the new search  configuration and if you want to learn more about   flattening operators in RxJS, and how they  differ from each other I have a series of videos   where I cover exactly those operators and you  can check this video out following the hint that   should appear right there above. But now let's move  forward to another mistake and the mistake number   two, it's a wrong usage of the takeUntil() or takeUntilDestroyed() operators for unsubscription  management. Using these operators can give you a  false sense of confidence that you successfully   prevented memory leaks, for example in my case  everything looks safe, because I use the takeUntilDestroyed() operator, which is supposed to handle on  subscription when the component is destroyed, right?   However approaches based on the takeUntil() operator they handle on subscription only for   the operators located before the takeUntil() and they  have no effect on the downstream subscriptions so   if the component is destroyed while the HTTP call  is pending it will not be cancelled which is not   necessarily a big issue for a single HTTP call but  if you have a long living subscription something   like websocket connection there, then you might  get a problem. So long story short if you choose   to handle unsubscription using strategy based on  the take and until operator then make sure that   you move this operator to the end of the pipe  operator chain, also you can set up a dedicated   Eslint rule in your project to ensure that neither  you or your colleagues will make this mistake in   the future. If you want to get more details about  exactly this use case you can check out my another   video about exactly this topic again there  will appear the hint and now let's move to the   next mistake. And mistake number three it's a  manual subscription in components, but before a   short disclaimer I'm not saying that you should  never subscribe manually it is not true true uh   sometimes we cannot avoid manual subscriptions  for example, in directives or in services. My point   is that in components we have much better options  to deliver data from observables to the component   view. For example in my case I could simply  remove the manual subscription like that and the   observable itself I can assign to some property  in the component and from here I have already two   options if you use signals in your application you  could convert the observable to a signal using the   toSignal() helper function, and I would say that in  the modern Angular, it would be a preferable way to   do but if you use the older angular version or if  for whatever reason you don't use signals in your   app you can always use the good old async pipe for  that you just have to import the async pipe in   the component Imports array, and then whenever you  need in a template, you can apply the async pipe to   the observable you actually need. In my case  it is users observable, so I can do it just   like that. the cool thing about any of these two  approaches is that besides automatic subscription   to the observable, they also perform automatic  unsubscription when the corresponding view is   destroyed. This means that I can simplify my users  observable even more by removing that takeUntilDestroyed() operator along with the destroy ref  token by the way if you are curious what is   destroyRef token and why it exists in Angular  again I have a dedicated video about that you can   check this out and yes in this tutorial there will  be quite a lot of references to my other videos   but nevertheless let's move forward and talk about  another mistake that we recently introduced during   this last refactoring. And mistake number four  it's executing observable logic more times than   we need, terrible title, but I couldn't come  up with anything better, in short the problem   is the following if I change the search config I  always see two identical HTTP calls in the console. This is something new because we didn't have  this be behavior before and the trick here is that   actually, each new subscription to the observable  triggers the execution of the whole operator pipe   chain and the creation of the value producer  for each subscriber. For this kind of behavior   we have a special term 'Cold observable' and this  is exactly my case as you remember I applied two   times the async pipe to the users observable so I  created two subscriptions, two subscriptions means   that two times will be executed the observable  logic, which leads to two HTTP calls, simple and   again there are a couple of ways how to fix it. First approach is the wrapping the code with   the 'if' block subscribe to the observable there  only once, and then assign the result from this   observable to the template variable that you can  use within this blog. Alternatively you can use   special operators that make your observable  hot making them share the latest observable   value with new subscribers. You can achieve that  using a group of operators like share() or shareReplay() and others, but in my opinion shareReplay() is the most popular and universal   one, and the idea here is to define how many value  emissions from the RxJs stream have to be replied   to new subscribers. Since I need only latest value  from the observable I define one, as an argument   so now when the new subscriber arrives the logic  before shareReplay() will not be executed. Instead   shareReplay() will just return the latest value that  was emitted to the stream. So now if we say this   change and try to find any other user once again, you can see that now I don't have duplicated HTTP   call which is exactly what I expected, but let's  move forward to mistake number five. And mistake   number five it is improper usage of the distinctUntilChanged() operator. To demonstrate this issue   I would like to expand the searchConfig$ observable  and let me very quickly break it down. Here you   can see the usage of the debounceTime() operator with  the value of 300 milliseconds. I needed to filter   out rapid successive emissions to the stream  allowing only the final value to be emitted after   the specified 300 milliseconds delay. In such a  way I can reduce the number of HTTP calls and the   server load. However it means that the user  can change the searchConfig and revert it back   within 300 milliseconds emitting the same config  again and it will cause the same HTTP call. This is   exactly what the distinctUntilChanged() operator should prevent, but as you can see it doesn't do   its job. The thing is that in this configuration  the distinctUntilChanged() operator works   properly only with primitive data like strings, boolean, numbers, etc. But here the stream emits   objects, and to compare if the object has changed  it uses strict comparison, however if you compare   two objects like this, you will always get  false, although the shape of the object is the   same, that's why if you emit non-primitive data  types in your observable and you use the distinctUntilChanged() operator there you have to use a  predicate function that allows you to compare   previous and current values more accurately by  comparing values of certain object keys or   make the deep object comparison. And by the way  Pro Tip if you need to track only one object key   you can shorten this notation by using another  operator called distinctUntilKeyChanged() and   then you just provide as a value the name of  the key you want to track so now let's save   our changes and try to reproduce their previous  behavior again. And you can see that this time the   HTTP call with the same search config has not been  performed which is actually cool as you can see   knowledge about fundamental data structures and  their differences is quite important in software   development and if you would like to learn more  about data structures algorithms and computer   science, check out platform called brilliant which  is sponsor of today's video what is special about   brilliant is that there you can find thousands  of interactive lessons and courses in computer   science, data analysis, programming and of course AI  so there you can learn not only computer science   and math fundamentals but also algorithmic  thinking and new programming languages like   for example python, learning on brilliant is really  easy and engaging because of the Hands-On approach   which helps you deeply understand Concepts to try  everything brilliant has to offer for free for a   full 30 days check the video description where you  can find a special link brilliant.org/decodedfrontend click on it and you will also get 20% off  for the annual premium subscription and now let's   get back to the video. And the last mistake, mistake  number six it is a per forming side effects in   the wrong place. In short, side effect is when a  function interacts and modifies anything outside   of its scope. This might make your code potentially  harder to understand harder to test and harder to   debug. Despite that, we cannot avoid side effects  completely because it is simply impossible for   example here in the map() operator besides reshaping  the config object I save also the config in the   localStorage this is a side effect, but I cannot  avoid it because it is part of my functionality. The problem here is not the side effect itself, but more the fact that looking at the code of   this observable I have no idea if it performs  side effect or not, and if yes then what exactly   will be effective, which component properties  will be modified and so on. That's why in RxJS  operators you have to use pure functions means  the functions that don't perform side effects, and if you need to perform them you have to do  it inside the special operator called tap(). This   is exactly what this operator is designed for so  in my case, the interaction with the localStorage   has to happen right there. So now I can immediately  see that the observable has side effects and I can   go and see what exactly happens there which makes  my code more clear and predictable. All right guys   thanks for watching, I hope this video was useful  and it will help you to improve the code quality   in your angular projects. What would be your  personal rating or the top let's say five mistakes that you see people do in RxJS code, it  would be be very interesting, please share this in   the comment section under this video. Otherwise if  you would like to support my channel please share   this video with your colleagues and friends,  in your social media profiles, and check out   my Advanced Angular video courses they can really  help you to bring your angular skills to a next   level. Otherwise guys I wish you productive week  ahead stay safe, and see you in the next video.
Info
Channel: Decoded Frontend
Views: 14,396
Rating: undefined out of 5
Keywords: rxjs, rxjs angular, rxjs mistakes in angular, reactive programming, reactive programming angular, angular rxjs switchmap, rxjs operators in angular with example, rxjs tap, web development, rxjs tips, Angular, Rxjs tutorial
Id: OhuRvfcw3Tw
Channel Id: undefined
Length: 18min 34sec (1114 seconds)
Published: Wed Jun 19 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.