Atomico — это современная библиотека для создания веб-компонентов с реактивным подходом, где состояние компонента и его свойства играют ключевую роль в управлении интерфейсом. Важно понимать, как свойства (props) обновляются и как это влияет на поведение компонента.
Каждое свойство, определённое через props, автоматически
становится реактивным. Это означает, что любое
изменение значения свойства приводит к повторному рендеру компонента.
Например:
import { c, useProp } from 'atomico';
function MyComponent({ count }) {
const [localCount, setLocalCount] = useProp('count');
return (
<host>
<button onCl ick={() => setLocalCount(localCount + 1)}>
{localCount}
</button>
</host>
);
}
MyComponent.props = {
count: Number
};
customElements.define('my-component', c(MyComponent));
В этом примере useProp связывает локальное состояние с
внешним свойством. Любое изменение count извне
автоматически обновляет локальное состояние и вызывает перерисовку.
Обновление свойств в Atomico основано на наблюдении за изменениями объекта props через Proxy. Когда значение свойства меняется:
useEffect с
зависимостями), они также выполняются.import { c, useProp, useEffect } from 'atomico';
function MyComponent({ name }) {
const [currentName, setCurrentName] = useProp('name');
useEffect(() => {
console.log(`Свойство name изменилось: ${currentName}`);
}, [currentName]);
return <host>{currentName}</host>;
}
MyComponent.props = {
name: String
};
useEffect с зависимостью [currentName]
гарантирует, что функция сработает только при реальном изменении
значения свойства.
Каждое свойство можно типизировать и задать значение по умолчанию. Это важно для корректного обновления:
MyComponent.props = {
name: {
type: String,
value: "Неизвестно"
},
count: {
type: Number,
value: 0
}
};
type определяет тип данных, который ожидается.value задаёт начальное значение, используемое до того,
как свойство будет установлено извне.Если новое значение не соответствует типу, Atomico пытается выполнить преобразование (например, строку в число). При несоответствии типу корректный рендер не гарантируется.
Atomico позволяет синхронизировать внешние изменения свойств
с внутренним состоянием компонента через useProp.
Внутренние изменения, выполненные через setLocalProp,
автоматически отражаются во внешнем свойстве:
function Counter({ count }) {
const [localCount, setLocalCount] = useProp('count');
const increment = () => setLocalCount(localCount + 1);
return <host><button onCl ick={increment}>{localCount}</button></host>;
}
Если родительский компонент изменяет count,
localCount обновится автоматически. Если компонент сам
меняет localCount, это изменение также отражается на
внешнем свойстве.
Atomico оптимизирует повторный рендер с помощью сравнения предыдущего и нового значения свойства. Если новое значение совпадает со старым, перерисовка не происходит. Это снижает нагрузку на DOM и предотвращает лишние эффекты.
Для массивов и объектов рекомендуется создавать новые экземпляры при изменении, так как реактивность основана на ссылках:
const [items, setItems] = useProp('items');
// Правильно
setItems([...items, newItem]);
// Неправильно, не вызовет обновление
items.push(newItem);
setItems(items);
В Atomico можно управлять, когда обновление свойства вызывает рендер,
используя флаг reflect. Если свойство не
должно автоматически отражаться в атрибутах DOM, его можно
отключить:
MyComponent.props = {
name: { type: String, reflect: false },
visible: { type: Boolean, reflect: true }
};
reflect: true — значение свойства синхронизируется с
атрибутом DOM.reflect: false — внутренние изменения свойства не
трогают DOM, но вызывают повторный рендер.Для сложных случаев, когда требуется реагировать на конкретные
изменения свойства, можно использовать наблюдатели
(watch):
MyComponent.props = {
count: {
type: Number,
watch: (oldValue, newValue) => {
console.log(`count изменился с ${oldValue} на ${newValue}`);
}
}
};
watch вызывается после изменения
значения, но до рендера. Это удобно для логирования или запуска
побочных эффектов.
Свойства Atomico хорошо сочетаются с состоянием из внешних
источников, таких как Redux, RxJS или обычные объекты. Для синхронизации
достаточно использовать useProp совместно с подпиской на
внешние данные:
import { fromEvent } from 'rxjs';
function InputComponent({ value }) {
const [inputValue, setInputValue] = useProp('value');
useEffect(() => {
const subscription = fromEvent(document, 'input')
.subscribe(event => setInputValue(event.target.value));
return () => subscription.unsubscribe();
}, []);
return <host><input value={inputValue} /></host>;
}
Обновление свойств в Atomico строится на реактивной
синхронизации между внешними и внутренними данными,
автоматическом перерендере и строгой типизации. Использование
useProp, watch, reflect и
правильная работа с объектами и массивами позволяют создавать
эффективные и предсказуемые компоненты, легко
интегрируемые с любыми внешними источниками данных.