Understanding @ngrx/component-store Selector Debouncing
Tags: angular, ngrx
Published: 2020-10-06 21:57:16

@ngrx/component-store selectors have a `debounce` option that lets the state *settle* before emitting. But what does this mean and how does it work?

# NgRx Component Store

I have started using [@ngrx/component-store](https://ngrx.io/guide/component-store) to manage component state in my applications and so far I am loving it! In this post I am not going to explain how or why to use `@ngrx/component-store` but if you want to know more check out this [video](https://www.youtube.com/watch?v=zMtubR7etsE) by [Alex Okrushko](https://twitter.com/AlexOkrushko).

## Debounce Selectors

In this post I want to take a closer look at the `{debounce}` config option for the `select` method. Here is what the docs say about debouncing.

> Selectors are synchronous by default, meaning that they emit the value immediately when subscribed to, and on every state change. Sometimes the preferred behavior would be to wait (or debounce) until the state "settles" (meaning all the changes within the current microtask occur) and only then emit the final value. In many cases, this would be the most performant way to read data from the ComponentStore, however its behavior might be surprising sometimes, as it won't emit a value until later on. This makes it harder to test such selectors.

At first I did not understand what this meant so I built an example in [Stackblitz](https://stackblitz.com/edit/ngrx-component-store-debouncing?devtoolsheight=33&file=src/app/app.component.ts) to see what difference the flag made to a selector.


## Demo App Setup

We setup the component store as part of the AppComponent with a boolean toggle state.

```ts
interface AppCompState {
  toggle: boolean;
}
```

We then create two selectors on this toggle, one which we debounce and the other that we do not.

```ts
update$ = this.select((s) => s.toggle, { debounce: false });

updateDebounced$ = this.select((s) => s.toggle, { debounce: true });
```

As the docs speak about selectors being synchronous I have created two methods that watch the toggle state and then toggle it back. A bit like a naughty child turning the TV back on as soon as you turn it off!

The important difference is that we include a `delay(0)` in the second toggler to make the `toggleState` call asynchronous. 

```ts
// Set up synchronous auto toggle back
this.select((s) => s.toggle)
  .pipe(take(1))
  .subscribe(() => this.toggleState());

// Set up asynchronous auto toggle back using delay(0)
this.select((s) => s.toggle)
  .pipe(delay(0), take(1))
  .subscribe(() => this.toggleState());
```

We trigger these actions by two different buttons in the demo app.

### Synchronous Updates

When we click on Update Sync only the selector with `debounce: false` emits any values. Without debouncing the selector emits every changed toggle value.

![Synchronou Updates](https://dev-to-uploads.s3.amazonaws.com/i/1pvc5xc3ihjp6al20xx1.png)

However, the selector that is debouncing emits no change. Why is this? The value of the toggle starts as true, gets set to false before being set back to true. This all happens synchronously, (in the same microtask) and is debounced by the [`debounceSync`](https://github.com/ngrx/platform/blob/master/modules/component-store/src/debounce-sync.ts) function. At the end of the microtask the value is still true and the selector does not emit. There is a `distintUntilChanged` in the select method that ensures this.


### Asynchronous Updates

When we click on Update Async both selectors now emit values. The `debounceSync` function, as the name suggests, only debounces synchronous updates. Now the debounced selector emits every toggle change as each occurs in a different microtask. 

![Asynchronous Update](https://dev-to-uploads.s3.amazonaws.com/i/ww76xyxmqx24geqldo43.png)

## What does this all mean?

### Perfomance

As the docs suggest using `debounce: true` can improve the performance of your app as the selectors will only emit new values at the end of a microtask. In our demo app this means the selector would not emit at all resulting in no further actions / re-rendering. Debouncing avoids unnecessary work.

### Consistency

State emitted by a debounced selector may be more consistent or logically correct. For example, if the selector relies on multiple properties, which are interdependent, then we want them to have reached a valid state before the selector emits. Setting `{debounce:true}` ensures we do not emit all the intermediary values which could originate from a temporary 'invalid state'.

## Next steps 

In my next post we will examine the `debounceSync` [source code](https://github.com/ngrx/platform/blob/master/modules/component-store/src/debounce-sync.ts) to see how this debouncing actually works.