Перейти к содержанию

Жизненный цикл компонента

Экземпляр компонента имеет жизненный цикл, который начинается, когда Angular инстанцирует класс компонента и отображает представление компонента вместе с его дочерними представлениями. Жизненный цикл продолжается при обнаружении изменений, так как Angular проверяет, когда изменяются свойства, связанные с данными, и обновляет представление и экземпляр компонента по мере необходимости.

Жизненный цикл заканчивается, когда Angular уничтожает экземпляр компонента и удаляет его отрисованный шаблон из DOM.

Директивы имеют аналогичный жизненный цикл, поскольку Angular создает, обновляет и уничтожает экземпляры в процессе выполнения.

Ваше приложение может использовать методы lifecycle hook для подключения к ключевым событиям жизненного цикла компонента или директивы для инициализации новых экземпляров, инициирования обнаружения изменений при необходимости, реагирования на обновления во время обнаружения изменений и очистки перед удалением экземпляров.

Предварительные условия

Перед работой с хуками жизненного цикла необходимо иметь базовое представление о следующем:

Реагирование на события жизненного цикла

Реагируйте на события жизненного цикла компонента или директивы, реализуя один или несколько интерфейсов lifecycle hook в библиотеке Angular core. Эти хуки дают вам возможность действовать на экземпляр компонента или директивы в нужный момент, когда Angular создает, обновляет или уничтожает этот экземпляр.

Каждый интерфейс определяет прототип для одного метода хука, имя которого является именем интерфейса с префиксом ng. Например, интерфейс OnInit имеет хук-метод с именем ngOnInit().

Если вы реализуете этот метод в классе вашего компонента или директивы, Angular вызовет его вскоре после первой проверки свойств ввода для этого компонента или директивы.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@Directive({ selector: '[appPeekABoo]' })
export class PeekABooDirective implements OnInit {
    constructor(private logger: LoggerService) {}

    // implement OnInit's `ngOnInit` method
    ngOnInit() {
        this.logIt('OnInit');
    }

    logIt(msg: string) {
        this.logger.log(`#${nextId++} ${msg}`);
    }
}

Вам не обязательно реализовывать все (или любые) хуки жизненного цикла, а только те, которые вам нужны.

Последовательность событий жизненного цикла

После того как ваше приложение инстанцирует компонент или директиву, вызывая его конструктор, Angular вызывает реализованные вами методы hook в соответствующий момент жизненного цикла этого экземпляра.

Angular выполняет методы hook в следующей последовательности. Используйте их для выполнения следующих видов операций.

Метод хука Назначение Тайминг
ngOnChanges() Реагирует, когда Angular устанавливает или сбрасывает привязанные к данным свойства ввода. Метод получает объект SimpleChanges с текущим и предыдущим значениями свойств. Это происходит часто, поэтому любые операции, выполняемые здесь, существенно влияют на производительность. Вызывается перед ngOnInit() (если у компонента есть связанные входы) и всякий раз, когда одно или несколько связанных с данными входных свойств изменяются. Если у вашего компонента нет входов или вы используете его, не предоставляя никаких входов, фреймворк не будет вызывать ngOnChanges().
ngOnInit() Инициализируйте директиву или компонент после того, как Angular впервые отобразит свойства, связанные с данными, и установит входные свойства директивы или компонента. Вызывается один раз, после первого ngOnChanges(). ngOnInit() вызывается даже тогда, когда ngOnChanges() не вызывается (что бывает, когда нет входов, связанных с шаблоном).
ngDoCheck() Обнаружение изменений, которые Angular не может или не хочет обнаружить самостоятельно, и принятие соответствующих мер. Вызывается сразу после ngOnChanges() при каждом запуске обнаружения изменений, и сразу после ngOnInit() при первом запуске.
ngAfterContentInit() Реагирует после того, как Angular проецирует внешнее содержимое в представление компонента или в представление, в котором находится директива. Вызывается один раз после первой ngDoCheck().
ngAfterContentChecked() Реагирует после того, как Angular проверит содержимое, спроецированное в директиву или компонент. Вызывается после ngAfterContentInit() и каждой последующей ngDoCheck().
ngAfterViewInit() Реагирует после того, как Angular инициализирует представления и дочерние представления компонента или представление, содержащее директиву. Вызывается один раз после первого ngAfterContentChecked().
ngAfterViewChecked() Реагирует после того, как Angular проверит представления и дочерние представления компонента или представление, содержащее директиву. Вызывается после ngAfterViewInit() и каждого последующего ngAfterContentChecked().
ngOnDestroy() Очистка непосредственно перед тем, как Angular уничтожит директиву или компонент. Отпишитесь от Observables и отсоедините обработчики событий, чтобы избежать утечек памяти. Вызывается непосредственно перед тем, как Angular уничтожит директиву или компонент.

Набор примеров жизненного цикла

Пример демонстрирует использование хуков жизненного цикла через серию упражнений, представленных в виде компонентов под управлением корневого AppComponent. В каждом случае родительский компонент служит испытательным стендом для детского компонента, который иллюстрирует один или несколько методов хуков жизненного цикла.

В следующей таблице перечислены упражнения с краткими описаниями. Пример кода также используется для иллюстрации конкретных задач в следующих разделах.

Компонент Подробности
Peek-a-boo Демонстрирует каждый хук жизненного цикла. Каждый метод хука записывает данные в экранный журнал.
Spy Показывает, как использовать хуки жизненного цикла с пользовательской директивой. Директива SpyDirective реализует хуки ngOnInit() и ngOnDestroy() и использует их, чтобы наблюдать и сообщать, когда элемент входит или выходит из текущего представления.
OnChanges Демонстрирует, как Angular вызывает хук ngOnChanges() каждый раз, когда изменяется одно из входных свойств компонента, и показывает, как интерпретировать объект changes, переданный в метод хука.
DoCheck Реализует метод ngDoCheck() с пользовательским обнаружением изменений. Посмотрите, как хук публикует изменения в журнале, чтобы узнать, как часто Angular вызывает этот хук.
AfterView Показывает, что Angular подразумевает под view. Демонстрирует хуки ngAfterViewInit() и ngAfterViewChecked().
AfterContent Показывает, как проецировать внешнее содержимое в компонент и как отличать проецируемое содержимое от дочерних элементов представления компонента. Демонстрирует хуки ngAfterContentInit() и ngAfterContentChecked().
Counter Демонстрирует комбинацию компонента и директивы, каждая из которых имеет свои собственные хуки.

Инициализация компонента или директивы

Используйте метод ngOnInit() для выполнения следующих задач инициализации.

Задачи инициализации Подробности
Выполнять сложные инициализации вне конструктора Компоненты должны быть дешевыми и безопасными в построении. Не следует, например, получать данные в конструкторе компонента. Не стоит беспокоиться, что новый компонент попытается связаться с удаленным сервером при создании в тестовом режиме или до того, как вы решите его отобразить. Функция ngOnInit() является хорошим местом для получения компонентом своих начальных данных. Для примера смотрите учебник Tour of Heroes.
Настройте компонент после того, как Angular установит входные свойства Конструкторы должны делать не больше, чем устанавливать начальные локальные переменные в простые значения. Помните, что входные свойства директивы, связанные с данными, не устанавливаются до после конструирования. Если вам нужно инициализировать директиву на основе этих свойств, установите их при выполнении ngOnInit(). Метод ngOnChanges() — это ваша первая возможность получить доступ к этим свойствам. Angular вызывает ngOnChanges() до ngOnInit(), а также много раз после этого. Он вызывает ngOnInit() только один раз.

Очистка при уничтожении экземпляра

Angular предоставляет несколько способов очистки при уничтожении экземпляра.

ngOnDestroy

Вы можете поместить логику очистки в ngOnDestroy(), логику, которая должна выполняться до того, как Angular уничтожит директиву.

Это место для освобождения ресурсов, которые не будут собраны автоматически. Если вы пренебрежете этим, то рискуете получить утечку памяти.

  • Отменить подписку на Observables и события DOM.
  • Остановите интервальные таймеры
  • Отмените регистрацию всех обратных вызовов, которые директива зарегистрировала в глобальных или прикладных службах.

Метод ngOnDestroy() также является временем для уведомления другой части приложения о том, что компонент уходит.

DestroyRef

В дополнение к ngOnDestroy(), вы можете внедрить в Angular DestroyRef и зарегистрировать функции обратного вызова, которые будут вызываться при уничтожении окружающего контекста. Это может быть полезно для создания многократно используемых утилит, требующих очистки.

Зарегистрируйте обратный вызов с DestroyRef:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Component(...)
class Counter {
    count = 0;
    constructor() {
        // Start a timer to increment the counter every second.
        const id = setInterval(() => this.count++, 1000);

        // Stop the timer when the component is destroyed.
        const destroyRef = inject(DestroyRef);
        destroyRef.onDestroy(() => clearInterval(id));
    }
}

Как и ngOnDestroy, DestroyRef работает в любом сервисе, директиве, компоненте или пайпе Angular.

takeUntilDestroyed

takeUntilDestroyed доступен для предварительного просмотра разработчиком. Он готов к тому, чтобы вы попробовали, но он может измениться до того, как станет стабильным.

При использовании RxJS Observables в компонентах или директивах вы можете захотеть завершить работу всех наблюдаемых при уничтожении компонента или директивы. Пакет Angular @angular/core/rxjs-interop предоставляет оператор takeUntilDestroyed для упрощения этой распространенной задачи:

1
data$ = http.get('...').pipe(takeUntilDestroyed());

По умолчанию takeUntilDestroyed должен быть вызван в контексте инъекции, чтобы он мог получить доступ к DestroyRef. Если контекст инъекции недоступен, вы можете явно указать DestroyRef.

Общие примеры

Следующие примеры демонстрируют последовательность вызовов и относительную частоту различных событий жизненного цикла, а также то, как хуки можно использовать отдельно или вместе для компонентов и директив.

Последовательность и частота всех событий жизненного цикла

Чтобы показать, как Angular вызывает хуки в ожидаемом порядке, PeekABooComponent демонстрирует все хуки в одном компоненте.

На практике вы редко, если вообще когда-либо, будете реализовывать все интерфейсы так, как это сделано в данном демонстрационном примере.

Следующий снимок отражает состояние журнала после того, как пользователь нажал кнопку Create…, а затем кнопку Destroy….

Peek-a-boo

Последовательность сообщений журнала соответствует предписанному порядку вызова хука:

Порядок вызова хука Сообщение журнала
1 OnChanges
2 OnInit
3 DoCheck
4 AfterContentInit
5 AfterContentChecked
6 AfterViewInit
7 AfterViewChecked
8 DoCheck
9 AfterContentChecked
10 AfterViewChecked
11 OnDestroy

Обратите внимание, что журнал подтверждает, что входные свойства (в данном случае свойство name) не имеют присвоенных значений при построении. Входные свойства доступны методу onInit() для дальнейшей инициализации.

Если бы пользователь нажал кнопку Update Hero, в журнале было бы показано еще одно OnChanges и еще две тройки DoCheck, AfterContentChecked и AfterViewChecked. Обратите внимание, что эти три хука срабатывают часто, поэтому важно сохранить их логику как можно более компактной.

Используйте директивы для наблюдения за DOM

Пример Spy демонстрирует, как использовать метод hook как для директив, так и для компонентов. Директива SpyDirective реализует два хука, ngOnInit() и ngOnDestroy(), чтобы обнаружить, когда наблюдаемый элемент находится в текущем представлении.

Этот шаблон применяет SpyDirective к div в повторителе ngFor hero, управляемом родительским SpyComponent.

Пример не выполняет никакой инициализации или очистки. Он просто отслеживает появление и исчезновение элемента в представлении, записывая, когда сама директива создается и уничтожается.

Подобная директива-шпион может дать представление об объекте DOM, который вы не можете изменить напрямую. Вы не можете получить доступ к реализации встроенного div или модифицировать сторонний компонент.

Однако у вас есть возможность наблюдать за этими элементами с помощью директивы.

Директива определяет хуки ngOnInit() и ngOnDestroy(), которые регистрируют сообщения для родителя с помощью внедренного LoggerService.

src/app/spy.directive.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
let nextId = 1;

// Spy on any element to which it is applied.
// Usage: <div appSpy>...</div>
@Directive({ selector: '[appSpy]' })
export class SpyDirective implements OnInit, OnDestroy {
    private id = nextId++;

    constructor(private logger: LoggerService) {}

    ngOnInit() {
        this.logger.log(`Spy #${this.id} onInit`);
    }

    ngOnDestroy() {
        this.logger.log(`Spy #${this.id} onDestroy`);
    }
}

Примените шпиона к любому встроенному или компонентному элементу и убедитесь, что он инициализируется и уничтожается одновременно с этим элементом. Здесь он прикреплен к повторяющемуся герою <p>:

src/app/spy.component.html
1
<p *ngFor="let hero of heroes" appSpy>{{hero}}</p>

Создание и уничтожение каждого шпиона отмечает появление и исчезновение присоединенного героя <p> записью в Hook Log. Добавление героя приводит к появлению нового героя <p>.

Шпионская функция ngOnInit() регистрирует это событие.

Кнопка Reset очищает список героев. Angular удаляет все элементы героя <p> из DOM и одновременно уничтожает их шпионские директивы.

Метод ngOnDestroy() шпиона сообщает о его последних минутах.

Используйте хуки компонентов и директив вместе

В этом примере CounterComponent использует метод ngOnChanges() для регистрации изменений каждый раз, когда родительский компонент увеличивает свое входное свойство counter.

Этот пример применяет SpyDirective из предыдущего примера к журналу CounterComponent, чтобы следить за созданием и уничтожением записей журнала.

Использование хуков обнаружения изменений

Angular вызывает метод ngOnChanges() компонента или директивы всякий раз, когда обнаруживает изменения вводных свойств. Пример onChanges демонстрирует это, отслеживая хук OnChanges().

on-changes.component.ts
1
2
3
4
5
6
7
8
ngOnChanges(changes: SimpleChanges) {
  for (const propName in changes) {
    const chng = changes[propName];
    const cur  = JSON.stringify(chng.currentValue);
    const prev = JSON.stringify(chng.previousValue);
    this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
  }
}

Метод ngOnChanges() принимает объект, который сопоставляет каждое измененное имя свойства с объектом SimpleChange, содержащим текущее и предыдущее значения свойства. Этот хук перебирает измененные свойства и записывает их в журнал.

Пример компонента OnChangesComponent имеет два входных свойства: hero и power.

src/app/on-changes.component.ts
1
2
@Input() hero!: Hero;
@Input() power = '';

Хост OnChangesParentComponent привязывается к ним следующим образом.

src/app/on-changes-parent.component.html
1
<on-changes [hero]="hero" [power]="power"></on-changes>

Вот пример в действии, когда пользователь вносит изменения.

OnChanges

Записи журнала появляются по мере изменения строкового значения свойства power. Заметьте, однако, что метод ngOnChanges() не перехватывает изменения hero.name. Это происходит потому, что Angular вызывает хук только при изменении значения входного свойства.

В данном случае hero — это входное свойство, а значение свойства hero — это ссылка на объект hero.

Ссылка на объект не изменилась, когда изменилось значение его собственного свойства name.

Реагирование на изменения представления

Поскольку Angular обходит иерархию представлений при обнаружении изменений, ему необходимо быть уверенным, что изменение в дочернем объекте не попытается вызвать изменение в его собственном родительском объекте. Такое изменение не будет отображено должным образом из-за того, как работает однонаправленный поток данных.

Если вам нужно внести изменение, которое инвертирует ожидаемый поток данных, вы должны запустить новый цикл обнаружения изменений, чтобы это изменение было отображено. Примеры иллюстрируют, как безопасно вносить такие изменения.

Пример AfterView исследует хуки AfterViewInit() и AfterViewChecked(), которые Angular вызывает после создания дочерних представлений компонента.

Вот дочернее представление, которое отображает имя героя в <input>:

ChildViewComponent
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Component({
    selector: 'app-child-view',
    template: `
        <label for="hero-name">Hero name: </label>
        <input
            type="text"
            id="hero-name"
            [(ngModel)]="hero"
        />
    `,
})
export class ChildViewComponent {
    hero = 'Magneta';
}

Компонент AfterViewComponent отображает это дочернее представление в своем шаблоне:

AfterViewComponent
1
2
3
4
5
template: `
  <div>child view begins</div>
    <app-child-view></app-child-view>
  <div>child view ends</div>
`;

Следующие хуки выполняют действия, основанные на изменении значений в дочернем представлении, которое можно получить только путем запроса дочернего представления с помощью свойства, украшенного @ViewChild.

AfterViewComponent (выдержки из класса)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
export class AfterViewComponent
    implements AfterViewChecked, AfterViewInit {
    private prevHero = '';

    // Query for a VIEW child of type `ChildViewComponent`
    @ViewChild(ChildViewComponent)
    viewChild!: ChildViewComponent;

    ngAfterViewInit() {
        // viewChild is set after the view has been initialized
        this.logIt('AfterViewInit');
        this.doSomething();
    }

    ngAfterViewChecked() {
        // viewChild is updated after the view has been checked
        if (this.prevHero === this.viewChild.hero) {
            this.logIt('AfterViewChecked (no change)');
        } else {
            this.prevHero = this.viewChild.hero;
            this.logIt('AfterViewChecked');
            this.doSomething();
        }
    }
    // ...
}

Ожидание перед обновлением представления

В этом примере метод doSomething() обновляет экран, когда имя героя превышает 10 символов, но ждет тик перед обновлением comment.

AfterViewComponent (doSomething)
1
2
3
4
5
6
7
8
// This surrogate for real business logic sets the `comment`
private doSomething() {
  const c = this.viewChild.hero.length > 10 ? "That's a long name" : '';
  if (c !== this.comment) {
    // Wait a tick because the component's view has already been checked
    this.logger.tick_then(() => this.comment = c);
  }
}

Оба хука AfterViewInit() и AfterViewChecked() срабатывают после составления представления компонента. Если вы измените код таким образом, чтобы хук обновлял свойство компонента comment, связанное с данными, немедленно, вы увидите, что Angular выбрасывает ошибку.

Оператор LoggerService.tick_then() откладывает обновление журнала на один оборот цикла JavaScript браузера, что запускает новый цикл обнаружения изменений.

Пишите бережливые методы хуков, чтобы избежать проблем с производительностью.

Когда вы запускаете пример AfterView, обратите внимание, как часто Angular вызывает AfterViewChecked() — часто тогда, когда нет никаких изменений, представляющих интерес. Будьте внимательны к тому, как много логики или вычислений вы вкладываете в один из этих методов.

AfterView

Реагирование на изменения проецируемого содержимого

Проекция контента — это способ импортировать HTML-контент извне компонента и вставить его в шаблон компонента в определенном месте. Определить проекцию содержимого в шаблоне можно по следующим признакам.

  • HTML между тегами элементов компонента
  • Наличие тегов <ng-content> в шаблоне компонента

Разработчики AngularJS знают эту технику как transclusion.

Пример AfterContent исследует хуки AfterContentInit() и AfterContentChecked(), которые Angular вызывает после того, как Angular проецирует внешний контент в компонент.

Рассмотрим эту вариацию на примере предыдущего AfterView. На этот раз вместо того, чтобы включать дочернее представление в шаблон, он импортирует содержимое из родительского хука AfterContentComponent.

Ниже приведен шаблон родителя.

AfterContentParentComponent (выдержка из шаблона)
1
2
3
`<after-content>
  <app-child></app-child>
</after-content>`;

Обратите внимание, что тег <app-child> находится между тегами <after-content>. Никогда не помещайте содержимое между тегами элементов компонента если вы не собираетесь проецировать это содержимое в компонент.

Теперь посмотрите на шаблон компонента.

AfterContentComponent (template)
1
2
3
4
5
template: `
  <div>projected content begins</div>
    <ng-content></ng-content>
  <div>projected content ends</div>
`;

Тег <ng-content> является заместителем для внешнего содержимого. Он указывает Angular, куда вставить это содержимое.

В данном случае проектируемый контент — это <app-child> от родителя.

Projected Content

Использование хуков AfterContent

Хуки AfterContent похожи на хуки AfterView. Ключевое различие заключается в дочернем компоненте.

  • Хуки AfterView касаются ViewChildren, дочерних компонентов, чьи теги элементов появляются внутри шаблона компонента.
  • Хуки AfterContent касаются ContentChildren, дочерних компонентов, которые Angular проецирует в компонент.

Следующие хуки AfterContent выполняют действия, основанные на изменении значений в дочерних компонентах контента, которые можно получить только запросив их с помощью свойства, украшенного @ContentChild.

AfterContentComponent (выдержки из класса)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
export class AfterContentComponent
    implements AfterContentChecked, AfterContentInit {
    private prevHero = '';
    comment = '';

    // Query for a CONTENT child of type `ChildComponent`
    @ContentChild(ChildComponent)
    contentChild!: ChildComponent;

    ngAfterContentInit() {
        // contentChild is set after the content has been initialized
        this.logIt('AfterContentInit');
        this.doSomething();
    }

    ngAfterContentChecked() {
        // contentChild is updated after the content has been checked
        if (this.prevHero === this.contentChild.hero) {
            this.logIt('AfterContentChecked (no change)');
        } else {
            this.prevHero = this.contentChild.hero;
            this.logIt('AfterContentChecked');
            this.doSomething();
        }
    }
    // ...
}

Нет необходимости ждать обновления контента

Метод doSomething() этого компонента немедленно обновляет свойство компонента comment, связанное с данными. Нет необходимости задерживать обновление для обеспечения правильного рендеринга.

Angular вызывает оба хука AfterContent перед вызовом любого из хуков AfterView. Angular завершает композицию проектируемого контента до завершения композиции представления этого компонента.

Между хуками AfterContent... и AfterView... есть небольшое окно, которое позволяет вам изменять представление хоста.

Определение пользовательской проверки изменений

Чтобы отслеживать изменения, которые происходят там, где ngOnChanges() их не поймает, реализуйте собственную проверку изменений, как показано в примере DoCheck. Этот пример показывает, как использовать хук ngDoCheck() для обнаружения изменений, которые Angular не отлавливает самостоятельно, и принятия соответствующих мер.

Пример DoCheck расширяет пример OnChanges следующим хуком ngDoCheck():

DoCheckComponent (ngDoCheck)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
ngDoCheck() {

  if (this.hero.name !== this.oldHeroName) {
    this.changeDetected = true;
    this.changeLog.push(`DoCheck: Hero name changed to "${this.hero.name}" from "${this.oldHeroName}"`);
    this.oldHeroName = this.hero.name;
  }

  if (this.power !== this.oldPower) {
    this.changeDetected = true;
    this.changeLog.push(`DoCheck: Power changed to "${this.power}" from "${this.oldPower}"`);
    this.oldPower = this.power;
  }

  if (this.changeDetected) {
      this.noChangeCount = 0;
  } else {
      // log that hook was called when there was no relevant change.
      const count = this.noChangeCount += 1;
      const noChangeMsg = `DoCheck called ${count}x when no change to hero or power`;
      if (count === 1) {
        // add new "no change" message
        this.changeLog.push(noChangeMsg);
      } else {
        // update last "no change" message
        this.changeLog[this.changeLog.length - 1] = noChangeMsg;
      }
  }

  this.changeDetected = false;
}

Этот код проверяет определенные значения, представляющие интерес, фиксируя и сравнивая их текущее состояние с предыдущими значениями. Он записывает специальное сообщение в журнал, когда нет существенных изменений в герое или силе, чтобы вы могли видеть, как часто вызывается DoCheck().

Результаты наглядны.

DoCheck

Хотя хук ngDoCheck() может определить, когда изменилось имя героя, это дорогой хук. Этот хук вызывается с огромной частотой — после каждого цикла обнаружения изменений, независимо от того, где произошло изменение. В данном примере он вызывается более двадцати раз, прежде чем пользователь сможет что-либо сделать.

Большинство этих начальных проверок запускается при первом рендеринге Angular несвязанных данных в другом месте страницы. Простое перемещение курсора в другой <input> вызывает вызов.

Относительно небольшое количество вызовов выявляет фактические изменения соответствующих данных.

Если вы используете этот хук, ваша реализация должна быть очень легковесной, иначе пострадает пользовательский опыт.

📅 28.02.2022

Комментарии