0%

angular-signal-effect

概述

今天来聊一聊Angular中的effect, 使用过React框架的同学对effect应该都不陌生,不过,Angular中的effect和React中的effect有所不同。

1
2
3
effect(() => {
console.log(`The current count is: ${count()}`);
});

effect函数特点

effect函数有如下特点:

  1. effect函数至少运行一次,无论effect函数中是否读取signal,也无论signal的值是否发生变化。
  2. 如果effect函数中读取了signal,那么当signal的值发生变化时,effect函数会再次运行。
  3. 如果effect函数中没有读取signal,那么effect函数只会运行一次。当signal变化时,effect函数不会再次运行。
  4. effect函数中不要更新signal的值,否则会导致死循环。(因为signal更新—>effect运行—>更新signal—>effect运行…)

比如下面这个effect函数,只会运行一次,因为effect函数中没有读取count

1
2
3
effect(() => {
console.log('This is a effect function');
});

比如下面这个effect函数,会导致死循环。

1
2
3
effect(() => {
count.set(count() + 1);
});

effect函数只能运行在injection context中(比如构造函数体内), 如果effect函数的使用位置不对(比如放到了ngOnInit函数中),那么Angular会报如下错误:关于injection context的更多信息,可以查看这里

1
core.mjs:6643 ERROR RuntimeError: NG0203: effect() can only be used within an injection context such as a constructor, a factory function, a field initializer, or a function used with `runInInjectionContext`. Find more at https://angular.dev/errors/NG0203

effect函数的使用场景

Effects are rarely needed in most application code, but may be useful in specific circumstances. Here are some examples of situations where an effect might be a good solution:

  • Logging data being displayed and when it changes, either for analytics or as a debugging tool.
  • Keeping data in sync with window.localStorage.
  • Adding custom DOM behavior that can’t be expressed with template syntax.
  • Performing custom rendering to a <canvas>, charting library, or other third party UI library.

Destroy effect

effect函数会在组件销毁时自动销毁,不需要手动清理。当然effect函数返回EffectRef对象,可以手动调用destroy方法销毁effect函数。

1
2
3
4
5
const effectRef = effect(() => {
console.log('This is a effect function');
});

effectRef.destroy();

untrack模式

https://angular.dev/guide/signals#reading-without-tracking-dependencies

cleanup function.

effect函数中可以定义一个清理函数onCleanup,当effect函数被销毁时,清理函数会被调用。下面的代码演示了如何使用onCleanup函数清理定时器。

1
2
3
4
5
6
7
8
9
10
effect((onCleanup) => {
const user = currentUser();
const timer = setTimeout(() => {
console.log(`1 second ago, the user became ${user}`);
}, 1000);

onCleanup(() => {
clearTimeout(timer);
});
});

Reference

  1. https://angular.dev/guide/signals#effects