OnPush components re-render whenever an `@Input()` receives a new object reference, even if that object is structurally identical to the previous one — a common source of wasted change-detection cycles in Angular trees where parents rebuild filter or config objects on every render. Wrapping the setter in a structural equality check breaks that reference-identity coupling: the input setter runs on every reference change, but expensive recomputation only happens when the actual field values differ. This is distinct from `distinctUntilChanged` on an Observable stream because it guards a plain `@Input()` binding, which has no operator pipeline to intercept.
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';interface Filters {status: readonly string[];minPrice: number;}function sameFilters(a: Filters, b: Filters): boolean {return (a.minPrice === b.minPrice &&a.status.length === b.status.length &&a.status.every((s, i) => s === b.status[i]));}@Component({selector: 'app-catalog',standalone: true,changeDetection: ChangeDetectionStrategy.OnPush,template: `<app-list [items]="visibleItems" />`,})export class CatalogComponent {private lastFilters: Filters | null = null;visibleItems: unknown[] = [];@Input() set filters(next: Filters) {if (this.lastFilters && sameFilters(this.lastFilters, next)) {return;}this.lastFilters = next;this.visibleItems = this.recompute(next);}private recompute(f: Filters): unknown[] {return [];}}