Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 834
Sergey Avatar asked Aug 06 '19 16:08

Sergey


1 Answers

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.

like image 153
Reactgular Avatar answered Sep 26 '22 08:09

Reactgular