Владислав Калачев

Signals в Angular: заменит ли это React Hooks?

7

Angular — тяжеловес. Он никогда не стремился быть «простым как React». Зато стабилен, мощен, корпоративен.

Но время меняется. Разработчики хотят меньше бойлерплейта, больше предсказуемости и меньше RxJS-танцев. Angular слышит. И вот — Signals.

Я работаю с Angular давно. Пробовал всё: NgRx, ComponentStore, BehaviorSubject, кастомные сервисы. Когда появились Signals, я не поверил, что это встроено в Angular. Без библиотек. Без подписок. Почти как в React. Только без магии.

Разберёмся, что это такое, зачем нужно, и правда ли Signals могут заменить React Hooks — или хотя бы сделать Angular таким же приятным в работе.

Что такое Signals?

Signals — это реактивные переменные. Они умеют:
— Хранить значение
— Давать доступ к нему через функцию: count()
— Изменяться через .set() или .update()
— Автоматически обновлять всех, кто от них зависит

Это — основа новой реактивной модели Angular. Не RxJS. Не костыли. Встроенная реактивность, прямо в ядре фреймворка.

Простой пример

import { signal, computed, effect } from '@angular/core';

const count = signal(0);
const double = computed(() => count() * 2);

effect(() => {
  console.log(`Count is: ${count()}, Double is: ${double()}`);
});

count.set(2); // автоматически вызовется effect

Ключевое отличие: вы не подписываетесь. Вы просто вызываете count() — и Angular сам понимает, кто кого слушает.

Почему это важно?

До Signals у нас было два пути:
1. Использовать @Input() и отслеживать изменения вручную
2. Использовать RxJS

Но RxJS в Angular — это:
— Много бойлерплейта
— Потенциальные утечки из-за subscribe()
— Сложный трекинг зависимостей
— Проблемы с читаемостью кода

Signals решают эти проблемы:
— Без подписок
— Без отписок
— Без ngOnDestroy
— Без Observable-операторов

Просто пишешь как обычный код — и оно работает.

Signals vs React Hooks

Сходство видно невооружённым глазом. Вот типичный компонент:

React:

const [count, setCount] = useState(0);
const double = useMemo(() => count * 2, [count]);

useEffect(() => {
  console.log(count);
}, [count]);

Angular + Signals:

const count = signal(0);
const double = computed(() => count() * 2);

effect(() => {
  console.log(count());
});

В React мы явно указываем зависимости. В Angular — не нужно. Сигналы сами строят граф зависимостей.

Разбор по пунктам

ОсобенностьReactAngular
ИнициализацияuseState()signal()
Побочные эффектыuseEffect()effect()
МемоизацияuseMemo()computed()
ОбновлениеsetState()set() или update()
ЗависимостиЯвные в массивеАвтоматические
Удаление эффектаВозвращаем функция из useEffectНе нужно — Angular сам удалит

Angular Signals проще. Меньше кода, меньше ошибок.

Когда это особенно удобно

1. Простой UI-стейт

Забываем про BehaviorSubject.

// Было:
const count$ = new BehaviorSubject(0);
count$.next(1);

// Стало:
const count = signal(0);
count.set(1);

2. Зависимые значения

const firstName = signal('John');
const lastName = signal('Doe');

const fullName = computed(() => `${firstName()} ${lastName()}`);

Измените firstName — и fullName пересчитается автоматически.

3. Побочные эффекты

effect(() => {
  console.log('User changed:', user());
});

Не нужно useEffect, takeUntil, ngOnDestroy. Просто effect().

Signals и шаблон

Вы можете использовать сигналы прямо в шаблоне Angular:

@Component({
  standalone: true,
  imports: [CommonModule],
  template: `
    <button (click)="count.update(c => c + 1)">Increment</button>
    <p>{{ count() }}</p>
  `,
})
export class CounterComponent {
  count = signal(0);
}

Обновляется мгновенно. И — внимание — без ChangeDetectionStrategy.OnPush.

А как же RxJS?

RxJS остаётся — но теперь его не нужно использовать в 100% случаев. Signals хорошо подходят:
— Для UI-состояния
— Для derived state (вычисляемых значений)
— Для изоляции логики в компонентах

RxJS нужен, когда:
— У вас стримы данных (вебсокеты, таймеры)
— Сложная цепочка операторов
— Множественные источники событий

Комбинирование:

Можно обернуть Observable в signal:

toSignal(this.http.get('/user'), { initialValue: null });

Это уже работает. А в Angular 18 будет ещё лучше — fromObservable и toObservable.

Signals в больших проектах

Я пробовал внедрить Signals в реальном проекте — админке на Angular 17. И вот что понял:

— Локальные компоненты стало писать проще
— Тестировать — быстрее
— Поддерживать — легче

Вместо отдельных сервисов со Subject, я держу сигналы прямо в компоненте. Без стейтовых либ, без отписок.

Пример — форма создания пользователя:

export class UserFormComponent {
  firstName = signal('');
  lastName = signal('');
  email = signal('');

  fullName = computed(() => `${this.firstName()} ${this.lastName()}`);
}

В шаблоне:

<input [value]="firstName()" (input)="firstName.set($event.target.value)" />
<p>Full name: {{ fullName() }}</p>

Ограничения

Чтобы не звучало как реклама — вот минусы:
— Пока не работает в директивах и пайпах
— Не умеет асинхронность из коробки (async надо вручную оборачивать)
— Придётся менять привычки, особенно если вы с RxJS

Но всё это уже в разработке — Signals активно развивают.

Стоит ли переписывать старый код?

Нет. Signals — это не миграция, а эволюция.

Начинайте использовать их в новых компонентах. Где нужно быстрое UI-состояние — используйте signal. Где сложная бизнес-логика — оставайтесь на NgRx или ComponentStore.

Angular всегда был «enterprise», и Signals тут не чтобы всё сломать, а чтобы облегчить жизнь.

Будущее

— Angular 18 уже улучшает поддержку Signals
NgRx адаптирует Signals (ngrx-signals)
— Команда Angular делает ставку на signals-first подход
— Signals могут заменить не только Hooks, но и state-менеджеры в UI-слое

Выводы

— Signals — это реактивные переменные внутри Angular
— Они проще, чем RxJS, и чище, чем Hooks
— Angular с ними становится понятнее, быстрее, современнее
— Не нужно переписывать всё. Достаточно начать использовать их в новых компонентах.

Мой опыт

Я был скептиком. Но после пары компонентов на Signals — не хочется возвращаться к BehaviorSubject. Это тот редкий случай, когда Angular стал проще, а не сложнее. Что дальше? Если ты хочешь почувствовать Angular по-новому — начни с Signals. Один компонент. Один signal. Скорее всего, дальше ты не остановишься.

Комментарии:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *