Rollback стратегии в Preact представляют собой набор методов управления состоянием приложения при возникновении ошибок или некорректных изменений данных. Они позволяют откатить изменения, возвращая интерфейс и данные в безопасное состояние без перезагрузки страницы. В Preact, как и в других реактивных библиотеках, управление состоянием тесно связано с виртуальным DOM, что делает применение rollback стратегий более гибким и эффективным.
Состояние компонентов в Preact обычно хранится через
хуки useState и useReducer. Изменение
состояния инициирует обновление виртуального DOM, после чего происходит
диффинг и отрисовка только изменившихся элементов. Ошибки в обработчиках
событий или асинхронных запросах могут привести к неконсистентности
UI.
Rollback стратегия решает эту проблему следующим образом:
Такой подход минимизирует влияние ошибок на пользовательский интерфейс и предотвращает «битые» состояния.
Простейший пример реализации rollback стратегии с использованием
useState:
import { h } from 'preact';
import { useState } from 'preact/hooks';
function Counter() {
const [count, setCount] = useState(0);
const [prevCount, setPrevCount] = useState(0);
const increment = () => {
setPrevCount(count);
try {
if (Math.random() < 0.3) throw new Error("Simulated error");
setCount(count + 1);
} catch (error) {
setCount(prevCount);
console.error("Rollback to previous state:", prevCount);
}
};
return (
<div>
<p>Count: {count}</p>
<button onCl ick={increment}>Increment</button>
</div>
);
}
В этом примере при каждом изменении сохраняется предыдущее состояние, которое используется для отката при возникновении ошибки.
Асинхронные операции, такие как запросы к API, требуют отдельной
обработки, поскольку ошибка может возникнуть после того, как состояние
уже изменено. В Preact это решается через комбинацию
useState и async/await:
import { h } from 'preact';
import { useState } from 'preact/hooks';
function DataFetcher() {
const [data, setData] = useState(null);
const [prevData, setPrevData] = useState(null);
const fetchData = async () => {
setPrevData(data);
try {
const response = await fetch('/api/data');
if (!response.ok) throw new Error("Failed to fetch");
const result = await response.json();
setData(result);
} catch (error) {
setData(prevData);
console.error("Rollback after fetch error", error);
}
};
return (
<div>
<button onCl ick={fetchData}>Load Data</button>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
Здесь prevData выполняет роль контрольной точки, к которой происходит откат в случае ошибки.
useReducer для сложных rollback стратегийДля более сложного состояния, особенно когда изменения касаются
нескольких полей или вложенных объектов, удобнее применять
useReducer.
import { h } from 'preact';
import { useReducer } from 'preact/hooks';
const initialState = { value: 0, prevValue: 0 };
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { prevValue: state.value, value: state.value + 1 };
case 'ROLLBACK':
return { ...state, value: state.prevValue };
default:
return state;
}
}
function AdvancedCounter() {
const [state, dispatch] = useReducer(reducer, initialState);
const increment = () => {
try {
if (Math.random() < 0.3) throw new Error("Random failure");
dispatch({ type: 'INCREMENT' });
} catch {
dispatch({ type: 'ROLLBACK' });
}
};
return (
<div>
<p>Value: {state.value}</p>
<button onCl ick={increment}>Increment</button>
</div>
);
}
Преимущество useReducer —
централизованная обработка всех изменений состояния, что облегчает
управление сложными rollback логиками.
Часто применяются оптимистические обновления, когда UI изменяется до подтверждения успешного завершения операции (например, POST-запроса). В этом случае rollback стратегия является обязательной:
Пример оптимистического обновления с rollback:
const optimisticUpdate = async (newValue) => {
setPrevValue(value);
setValue(newValue);
try {
const response = await apiUpdate(newValue);
if (!response.ok) throw new Error("API error");
} catch {
setValue(prevValue);
}
};
useState, для сложного
— useReducer.Rollback стратегии позволяют создавать интерфейсы, устойчивые к ошибкам, и поддерживают предсказуемость поведения компонентов.