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. Если операция
завершается неудачно, состояние откатывается к исходному значению.
При работе с комплексными формами или массивами объектов важно сохранять не только отдельные значения, но и целую структуру состояния. Используется глубокое копирование:
@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.
Особенности возникают при обновлении массивов данных. В Stencil
массивы отслеживаются через @State, и любое изменение
массива вызывает перерендер компонента. Для rollback:
@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 стратегия позволяет безопасно реализовать этот подход. Важные шаги:
@Watch
и событиямиRollback стратегии также применимы при отслеживании изменений
свойств через @Watch и генерации пользовательских
событий:
@Prop() value: string;
@State() internalValue: string;
@Watch('value')
onValueChange(newValue: string, oldValue: string) {
this.internalValue = newValue;
}
// при ошибке асинхронной операции
this.internalValue = oldValue; // rollback
Это позволяет держать внутреннее состояние компонента синхронизированным с внешними свойствами, обеспечивая откат при сбоях.
В крупных приложениях лучше выносить логику rollback в отдельные сервисы или менеджеры состояния. Это позволяет централизованно обрабатывать ошибки и унифицировать поведение всех компонентов при неудачных запросах. Такой подход особенно полезен при работе с глобальными сторонами состояния через Stencil Store или интеграцию с Redux-подобными системами.
Rollback стратегии в Stencil обеспечивают устойчивость интерфейса и консистентность данных, предотвращая неожиданные состояния и улучшая UX при асинхронных операциях. Основная логика всегда сводится к сохранению предыдущего состояния, оптимистическому обновлению и корректному восстановлению при ошибке.