Rollback стратегии

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

Inferno не навязывает архитектурных паттернов, поэтому rollback реализуется на уровне управления состоянием, асинхронной логики и жизненного цикла компонентов.


Основы управления состоянием и отката

Ключевым объектом rollback является снимок состояния. Состояние может храниться:

  • локально в компоненте,
  • во внешнем сторе (Redux, Zustand, MobX),
  • в кастомных state-менеджерах.

Rollback предполагает:

  • сохранение предыдущего состояния;
  • выполнение изменения;
  • восстановление сохранённого состояния при ошибке.
const prevState = this.state;

this.setState({ loading: true });

apiCall()
  .then(data => this.setState({ data, loading: false }))
  .catch(() => this.setState(prevState));

В Inferno важно учитывать, что setState не является батчированным по умолчанию так агрессивно, как в React, поэтому сохранение состояния должно быть атомарным.


Rollback при оптимистичных обновлениях

Оптимистичное обновление — основной сценарий использования rollback-стратегий.

Последовательность:

  1. UI обновляется сразу.
  2. Отправляется запрос.
  3. При ошибке происходит откат.
function toggleLike(postId) {
  const snapshot = store.getState();

  store.setState(state => ({
    posts: state.posts.map(p =>
      p.id === postId ? { ...p, liked: !p.liked } : p
    )
  }));

  return api.toggleLike(postId).catch(() => {
    store.setState(snapshot);
  });
}

Ключевые моменты:

  • снимок должен быть неизменяемым;
  • откат должен выполняться синхронно;
  • побочные эффекты не должны повторяться.

Многоуровневые rollback-стратегии

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

const history = [];

function applyChange(changeFn) {
  history.push(store.getState());
  store.setState(changeFn);
}

function rollback() {
  const prev = history.pop();
  if (prev) {
    store.setState(prev);
  }
}

Такой подход позволяет:

  • откатывать несколько операций;
  • реализовывать undo/redo;
  • восстанавливать состояние при крашах.

Inferno не ограничивает глубину истории, но контроль памяти остаётся на стороне разработчика.


Rollback и асинхронные эффекты

Inferno не имеет встроенного хука аналогичного useEffect, но использует жизненный цикл компонентов и функциональные компоненты с внешними эффектами.

Проблема возникает, когда:

  • асинхронный запрос завершается после размонтирования компонента;
  • состояние обновляется некорректно.

Решение — привязка rollback к флагу активности.

let active = true;

fetchData()
  .then(data => {
    if (active) setData(data);
  })
  .catch(() => {
    if (active) rollback();
  });

return () => {
  active = false;
};

Rollback должен учитывать не только состояние, но и валидность контекста выполнения.


Rollback в транзакционных сценариях

Для сложных операций используется транзакционная модель.

function transaction(actions) {
  const snapshot = store.getState();

  try {
    actions.forEach(fn => fn());
  } catch (e) {
    store.setState(snapshot);
    throw e;
  }
}

Применяется при:

  • массовых изменениях состояния;
  • синхронных вычислениях;
  • взаимодействии с несколькими источниками данных.

Inferno, благодаря синхронной природе рендера, хорошо подходит для таких транзакций.


Интеграция rollback со сторонними стор-менеджерами

Redux:

  • используется middleware;
  • состояние восстанавливается через dispatch.
const rollbackMiddleware = store => next => action => {
  const snapshot = store.getState();
  try {
    return next(action);
  } catch {
    store.dispatch({ type: 'ROLLBACK', payload: snapshot });
  }
};

MobX:

  • применяется runInAction;
  • снимки хранятся вручную.

Zustand:

  • простая сериализация состояния;
  • откат через set(snapshot, true).

Rollback и производительность Inferno

Inferno минимизирует количество обновлений DOM, но rollback может:

  • вызывать полную перерисовку;
  • сбрасывать мемоизированные вычисления.

Рекомендации:

  • хранить минимальные снимки;
  • не сохранять derived state;
  • избегать глубоких копий без необходимости.

Использование Object.freeze для snapshot предотвращает случайные мутации и упрощает отладку.


Ошибки проектирования rollback-логики

Распространённые проблемы:

  • сохранение ссылочного состояния без клонирования;
  • повторный rollback одного и того же snapshot;
  • откат после частичного побочного эффекта;
  • смешивание rollback и retry логики.

Rollback должен быть детерминированным и идемпотентным.


Связь rollback-стратегий с философией Inferno

Inferno ориентирован на:

  • явное управление;
  • минимальный runtime;
  • предсказуемый рендеринг.

Rollback-стратегии вписываются в эту философию, когда:

  • состояние контролируется явно;
  • ошибки обрабатываются на уровне логики, а не фреймворка;
  • восстановление состояния является частью архитектуры, а не исключением.