Декларативный vs Императивный UI
Декларативный UI описывает что должно быть отображено (React, Vue); императивный — пошагово как изменить DOM (прямые манипуляции через document.getElementById, jQuery).
Зачем нужно
Понимание этого различия объясняет, почему React так устроен и почему прямые DOM-манипуляции в React-коде считаются антипаттерном. Декларативный подход перекладывает управление DOM на фреймворк: разработчик описывает желаемое состояние, React находит минимальный набор изменений (через Virtual DOM). Это снижает когнитивную нагрузку и устраняет целый класс ошибок.
Где используется
- Декларативный: React, Vue, Svelte, SwiftUI, Flutter, Jetpack Compose
- Императивный: vanilla JavaScript, jQuery, прямые DOM API, анимационные библиотеки (GSAP)
- Гибрид:
useRefв React для доступа к DOM при необходимости (фокус, canvas, сторонние библиотеки)
Сравнение подходов
Задача: показать/скрыть элемент
// Императивный подход (jQuery/vanilla JS)
// Описываем КАК изменить DOM
let isVisible = false;
document.getElementById('toggle').addEventListener('click', () => {
isVisible = !isVisible;
const content = document.getElementById('content');
if (isVisible) {
content.style.display = 'block'; // явно управляем DOM
content.classList.add('visible');
} else {
content.style.display = 'none';
content.classList.remove('visible');
}
const button = document.getElementById('toggle');
button.textContent = isVisible ? 'Скрыть' : 'Показать';
});
// Декларативный подход (React)
// Описываем ЧТО должно быть — React сам управляет DOM
function Toggle() {
const [isVisible, setIsVisible] = useState(false);
// UI — функция от состояния: UI = f(state)
return (
<div>
<button onClick={ => setIsVisible(prev => !prev)}>
{isVisible ? 'Скрыть' : 'Показать'}
</button>
{isVisible && <div className="content">Содержимое</div>}
</div>
);
}
Задача: список с добавлением элементов
// Императивный
const ul = document.getElementById('list');
const items = ;
function addItem(text) {
items.push(text);
const li = document.createElement('li');
li.textContent = text;
ul.appendChild(li); // ручное обновление DOM
}
function removeItem(index) {
items.splice(index, 1);
ul.querySelectorAll('li')[index].remove(); // опасно — индексы сдвигаются
}
// Декларативный
function ItemList() {
const [items, setItems] = useState();
const addItem = (text) => {
setItems(prev => [...prev, { id: crypto.randomUUID, text }]);
// React сам обновит DOM минимальным изменением
};
const removeItem = (id) => {
setItems(prev => prev.filter(item => item.id !== id));
};
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.text()}
<button onClick={ => removeItem(item.id)}>Удалить</button>
</li>
))}
</ul>
);
}
Когда нужен императивный доступ в React
// useRef — легальный «выход» для DOM-манипуляций
function AutoFocusInput() {
const inputRef = useRef(null);
useEffect(() => {
// Фокус на элемент — оправданная императивная операция
inputRef.current?.focus();
}, );
return <input ref={inputRef} />;
}
Частые ошибки
- Прямое обращение к DOM в React —
document.querySelector('.my-button').style.display = 'none'— обходит Virtual DOM и приводит к рассинхронизации. - Перемешивание парадигм — попытка управлять состоянием через DOM и через React одновременно.
- Использование
refвместо state — если изменение данных должно вызывать перерендер, нуженuseState, неuseRef.