Sergey
Sergey

Reputation: 7692

Angular markForCheck vs detectChanges

I'll start this question from notion that I've seen a similar question on StackOverflow, but that question had only answer for the difference.

What I'm asking is what should I use depending on situation and what drawbacks one or another method may have.

I know that detectChanges runs immediate change detection cycle on an element and its children, meanwhile markForCheck only marks current element and its ancestors as dirty and that they should be checked on the next change detection cycle.

I'm asking this mostly because I don't feel like I should always use markForCheck in async calls.

For example I have an InputComponent which is a wrapper for a regular HTML input. This InputComponent has ChangeDetectionStrategy.OnPush enabled.

When I make an asynchronous call to the server and get the data I need to run the change detection on that InputComponent to update a list of options and I have two options for that.

First (what I feel I should be using) is detectChanges because it would apply checks only for this exact component, whilst markForCheck would cause the whole tree branch to be checked.

So what should I use and do I need to use markForCheck ever and why?

Upvotes: 10

Views: 11037

Answers (2)

Sergey
Sergey

Reputation: 7692

Many years have passed since the time this question was asked. And some things have changed.

Most of the people who are interested in this question right now are better off with using signals.

They are simple. They are effective. They trigger change detection upon update (since Angular v18) even if an event changing the value originates from outside the Angular zone. Also, since Angular v18 you can enable experimental zoneless change detection and it will work just fine.

Furthermore, when a signal's value is updated – only components using that signal will be checked for changes (given that your application uses OnPush in all components, since it's still powered by the old change detection).

This makes the use of signals to be more like a silver bullet compared to markForCheck and detectChanges.

In future, we will get signal components, and they will be even more performant while you wouldn't have to change your code if you used signals for state-holding props that need to be rendered in templates and do update (if you render a static value that doesn't change – you can skip signals as it will be rendered anyway).


Regarding "never use detectChanges", I only partially agree with the statement. If you are 100% sure you need this, you know how change detection works in Angular, you can use it. It's totally fine.

But, for most people and most use cases markForCheck is the "go to" solution that can never hurt and will work perfectly in 99.99999% scenarios.

Upvotes: 1

Reactgular
Reactgular

Reputation: 54821

What I'm asking is what should I use depending on situation and what drawbacks one or another method may have.

You should never call detectChanges().

There isn't a good edge case where detectChanges() offers value to the developer. It's usually used inside projects where immutability, state management and mutation of the component have not been well managed by the programmer.

All source code that needs detectChanges() can be rewritten so that it's not required.

On the other hand, markForCheck() does have good edge cases where it should be used.

I'm asking this mostly because I don't feel like I should always use markForCheck in async calls.

You will often find a reference to this near source code that calls markForCheck().

@Component({...})
export class ExampleComponent {
    //......
    public function work() {
        this.httpClient.get(...).subscribe(resp => 
            this.data = resp.data;
            this.changeDetectorRef.markForCheck();
        });
    }
}

In functional programming, a reference to this is impure and mutates an external state outside the scope of the function. Breaking from functional programming best practices introduces problems that require fixes to keep everything working. If you write only pure functions with your async operations you never have to call markForCheck(), but once you introduce a this reference the components state is mutated and the view needs to be notified.

There is nothing wrong with the above, but at the sametime excessive usage of this in RxJS subscriptions creates source code that can be difficult to maintain.

It's better to rewrite your source code to use reactive programming, and use the async pipe in the template. The key is to create components that are stateless so that a properties on the component don't need to be updated. Everything is done as a reactive stream.

@Component({
    template: `<ng-container *ngIf="data$ | async as data">
               <!-- stuff -->
               </ng-container>`,
    // .....
})
export class ExampleComponent {
    public data$: Observable<any>;

    public function work() {
        this.data$ = this.httpClient.get(...).pipe(shareReplay(1));
    }
}

If you design your components to be stateless and use RxJS for all of your data processing, then there shouldn't be a requirement to use markForCheck(). Even when you listen for DOM events the data can be piped to other observables to avoid using this.

While there will be times when you have to call markForCheck(). I recommend that you stop and rethink your approach to avoid the use of it, because there should be another way that doesn't require it.

Upvotes: 4

Related Questions