Rollback стратегии

Rollback стратегия в контексте Stencil используется для обеспечения устойчивости пользовательского интерфейса при работе с асинхронными операциями, такими как запросы к серверу или обновление состояния приложения. Основная цель — предотвратить неконсистентное отображение данных и обеспечить корректное откатывание изменений в случае ошибки.

Асинхронные операции и состояние компонента

В Stencil состояние компонента управляется с помощью @State и @Prop. При изменении состояния компонент перерисовывается, и любое асинхронное действие может повлиять на UI. Стандартная проблема: пользователь инициирует действие, интерфейс обновляется мгновенно, но запрос к серверу завершается ошибкой. Без rollback стратегии данные могут остаться в неконсистентном состоянии.

Пример базового компонента с состоянием:

import { Component, h, State } from '@stencil/core';

@Component({
  tag: 'user-profile',
  styleUrl: 'user-profile.css',
  shadow: true,
})
export class UserProfile {
  @State() userName: string = 'Anonymous';

  async updateUserName(newName: string) {
    const previousName = this.userName;
    this.userName = newName;

    try {
      await fakeApiUpdate(newName);
    } catch (error) {
      this.userName = previousName; // rollback
      console.error('Ошибка обновления имени:', error);
    }
  }

  render() {
    return (
      <div>
        <p>Имя пользователя: {this.userName}</p>
        <button onCl ick={() => this.updateUserName('Alice')}>Изменить имя</button>
      </div>
    );
  }
}

async function fakeApiUpdate(name: string) {
  if (Math.random() > 0.5) throw new Error('Случайная ошибка сервера');
  return Promise.resolve();
}

В этом примере ключевой момент — сохранение предыдущего состояния previousName. Если операция завершается неудачно, состояние откатывается к исходному значению.

Использование Rollback с несколькими полями

При работе с комплексными формами или массивами объектов важно сохранять не только отдельные значения, но и целую структуру состояния. Используется глубокое копирование:

@State() user = {
  name: 'Anonymous',
  age: 30,
};

async updateUser(newUserData: Partial<typeof this.user>) {
  const previousState = { ...this.user }; // поверхностное копирование
  this.user = { ...this.user, ...newUserData };

  try {
    await fakeApiUpdateUser(this.user);
  } catch (error) {
    this.user = previousState; // откат к предыдущему состоянию
  }
}

Для вложенных объектов нужно использовать глубокое клонирование, например через structuredClone или библиотеки типа lodash.

Rollback для списков и коллекций

Особенности возникают при обновлении массивов данных. В Stencil массивы отслеживаются через @State, и любое изменение массива вызывает перерендер компонента. Для rollback:

  1. Сохраняется копия массива до изменения.
  2. В случае ошибки массив восстанавливается.
@State() items: string[] = ['Item1', 'Item2'];

async addItem(newItem: string) {
  const previousItems = [...this.items];
  this.items = [...this.items, newItem];

  try {
    await fakeApiAddItem(newItem);
  } catch (error) {
    this.items = previousItems; // восстановление
  }
}

Оптимистическое обновление с откатом

Оптимистическое обновление — когда UI изменяется сразу, еще до завершения асинхронной операции. Rollback стратегия позволяет безопасно реализовать этот подход. Важные шаги:

  • Сохранение состояния до изменения.
  • Обновление состояния для мгновенного отклика интерфейса.
  • Обработка ошибок и восстановление предыдущего состояния.

Важные практики

  • Для всех асинхронных операций, влияющих на UI, предусматривать rollback.
  • Использовать отдельные функции для сохранения и восстановления состояния.
  • Для сложных структур данных применять глубокое клонирование.
  • Логировать ошибки для последующего анализа.

Интеграция с @Watch и событиями

Rollback стратегии также применимы при отслеживании изменений свойств через @Watch и генерации пользовательских событий:

@Prop() value: string;
@State() internalValue: string;

@Watch('value')
onValueChange(newValue: string, oldValue: string) {
  this.internalValue = newValue;
}

// при ошибке асинхронной операции
this.internalValue = oldValue; // rollback

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

Использование сервисов для управления rollback

В крупных приложениях лучше выносить логику rollback в отдельные сервисы или менеджеры состояния. Это позволяет централизованно обрабатывать ошибки и унифицировать поведение всех компонентов при неудачных запросах. Такой подход особенно полезен при работе с глобальными сторонами состояния через Stencil Store или интеграцию с Redux-подобными системами.


Rollback стратегии в Stencil обеспечивают устойчивость интерфейса и консистентность данных, предотвращая неожиданные состояния и улучшая UX при асинхронных операциях. Основная логика всегда сводится к сохранению предыдущего состояния, оптимистическому обновлению и корректному восстановлению при ошибке.