November 7, 2019
Most developers are aware of Angular’s super-fast performance when it comes to application development. But as projects become more complex and deadlines lurk into your calendar, it is not uncommon for some people to run into problems. In order to provide effective angular development services, it is best to know exactly what factors affect your application’s performance and what you can do about them.
Simply put, web app performance matters—perhaps a lot more than you may think. You want your users to be engaged with both your site and your content. Otherwise, they will just stop coming back. Slowness and errors are our main enemy here. If you want to bring down those ugly bounce rates, keep on reading this article.
Tip #1: Reduce Change Detection
While not the most low-key tip, it is a perfectly good starting point. Angular loves to run checks in your application to know whether or not it should update the state of a component. This process is known as change detection, which is run after several asynchronous operations, such as event triggers and Ajax requests. Every component has its own change detector, which is just waiting for a value to change to re-render the component.
By default, Angular 7 runs a change detection protocol after every single user interaction. If you have done nothing about this before, your app is probably going frenzy with all the requests. The best solution is to grab the bull by the horns and signal Angular when an update is made on a component subtree. Then, allow the component to bypass change detection.
Now that you understand the concept, let’s put it to practice. If you want to command Angular to check the input if and only if the data is modified, simply change the setting from
changeDetection property to
ChangeDetectionStrategy.OnPush. You can take a look at the sample code for this here.
If that is not enough for you and you need just a bit more control, there is another option that requires just a little extra effort. Whenever you don’t want Angular to run checks on a specific component at all, try injecting the
ChangeDetectorRef service in your code. You can see how it’s done here.
Tip #2: Handling Errors
Sometimes loading spinners get confused and end up rolling infinitely without getting anywhere. Certainly not the developer’s fault, right? Well, if you bother to open up the console, you will most likely see an error log telling the story of a request that just gave up and failed. The truth is that these error cases are often left aside during the development process, but there is a very good reason why you should go back and fix them: API calls.
You may be aware of the pre-established patterns designed within NgRx to defend your app from unhandled errors. Nonetheless, these won’t matter as much when taking into account that API calls are done in Effects, which might stop listening for Actions if errors get in the way. You can check out a basic API call within an Effect here.
If you want all of your Effects to produce an Action unless they are explicitly told not to do so, you have to make sure that every
catchError operator results in the desired action. To fix this, every API call requires a pattern with three actions: Action to trigger the Effect, Action to wrap a successful result (suffix:
Success), and Action to reflex error response (suffix:
Tip #3: Optimistic User Interface interactions
The best way to explain this is with the classic “Add to cart” functionality. Whenever a user clicks that button, they expect the cart’s item count to increase immediately. As developers, we have to assume that this request will have success at the backend, even before receiving a response from the server.
That is why these updates are called “optimistic”—if everything is bound to go right, there is no need to make users wait for visual confirmation of their actions. On the off-chance that the server returns an error, a simple protocol to remove the item from the cart along with a quick notification to the user will do the trick in most cases.
Optimistic updates are nothing but effect and reducer implementations. You can use the
AddItem action to trigger the effect and also send the item to the reducer’s list. From here, the
AddItemError actions can take care of the rest of the process. Take a look at the code here. Just don’t use this approach for the Buy Button, for obvious reasons.
Tip #4: Bypassing the Fetch Products Response
Putting a lot of hours into those snappy page loads will get you far, but keeping data on the browser’s localStorage will get you even further. NgRx’s meta-reducers are a great way to use middleware to check for actions and state pairs in the app. However, remember not to include any sensitive data in localStorage—that could really ruin your site’s UX for good.
Getting this done is pretty simple. Locate the
productSync meta-reducer and check for the INIT and UPDATE actions, which are dispatched by NgRx. The INIT action should be dispatched whenever the Store is initialized, while UPDATE is for adding and removing feature reducers. You can just ignore the UPDATE actions and read from the
localStorage on INIT. After that, you should try to write back on any other action and measure your results.
In a nutshell, this will let you feed the store state from
localStorage, and you can forget all about the
FetchProducts response. As long as the data isn’t too old and it makes sense to show, everything should go just fine. If you want a more complicated and detailed way to do this, check out the ngrx-store-localstorage library. Otherwise, here is some sample code.
Today, software development is not just about making apps faster, but also making them feel faster. This article is proof that, with minimal effort, you can get some valuable results after just a few hours of work. Optimizing techniques for Angular are popping out everywhere, so don’t stop looking out for them. Happy coding to you.