Signals: Effect cleanup

You’re probably familiar with ngOnDestroy for Angular components/pipes/directives, which cleans things up when an Angular object is destroyed. A typical use case is to unsubscribe from RxJs subscriptions (though there are better-suited tools for this now) or cancel a timer.

Angular Signals introduced the effect() function to run side-effects, including RxJs Observables or timers. As a result, we might want to cancel a timer or an RxJS-based task when the effect runs again in the future, and luckily for us, Angular supports a cleanup function for our effects.

To enable the cleanup function, we pass it as a parameter to the callback registered in our effect:

Then we call it and pass our cleanup code as a param to it using an arrow function:

Here is the full example in action on Stackblitz. This example destroys a component and clears its timer thanks to the effect cleanup function:

Signals custom equality functions

Angular Signals are growing almost every day with new features and new ways of building Angular applications. If you’re still unsure about signals and want to catch up, you can watch this Angular Signals workshop recording on Youtube or sign up for my continuously updated Angular Signals course.

Today, let’s explore how Angular determines if the value of a signal has changed. If we create the following signal with a default value of “Hello”:

Then calling name.set("Hello") does not trigger any updates for any of the consumers of our name signal. That’s because Angular compares the “new” value with the previous one using a === comparison, and "Hello" === "Hello" is true, so Angular doesn’t trigger any change.

What about objects? When comparing objects with ===, we’re comparing references, meaning “Are the two variables pointing at the same object?” We can verify that easily with a few lines of Javascript running in a browser console:

This leads us to the custom signal equality function. If we want to do a deep comparison instead of just comparing references, we can instruct Angular to do so by providing our own comparison function. Here is the example used by the Angular team:

Of course, we don’t need lodash to implement custom equality functions. For example, we want to create a signal that only emits updates when the current user ID changes. We don’t care if the user’s name or email changes, just the ID (which would indicate that a new user logged in, most likely):

With this approach, the signal doesn’t trigger any updates if we change values that aren’t the ID. For instance, we can secretly turn our user into Batman (and note that I do create a new object by making a copy of the current one, which would make === return false):

And yet my user remains known as Bruce after turning him into Batman (full example here on Stackblitz):

Why use custom equality functions?

Performance could be one thing: if we have a signal that stores an object and know that the only change we make to that object is one or two specific properties, then creating a custom equality function on these properties would make perfect sense. Even more so if the properties are in nested objects, though I wouldn’t recommend using deeply nested objects as signal values, keeping our values as flat as possible instead.