SkateJS предоставляет гибкий и декларативный подход к созданию веб-компонентов, включая управление их состояниями. Одной из важных задач при разработке компонентов является обработка состояний загрузки и ошибок, особенно при работе с асинхронными операциями, такими как запросы к API или динамическая загрузка данных.
SkateJS использует реактивные свойства для отслеживания состояния
компонента. Свойства объявляются через объект props и могут
включать различные типы значений, включая boolean,
string, number и object. Для
управления состояниями загрузки и ошибок чаще всего используют отдельные
свойства:
import { define, props, h, Component } from 'skatejs';
class DataLoader extends Component {
static props = {
loading: props.boolean({ default: false }),
error: props.string(),
data: props.array()
};
connectedCallback() {
super.connectedCallback();
this.fetchData();
}
async fetchData() {
this.loading = true;
this.error = '';
try {
const response = await fetch('https://api.example.com/items');
if (!response.ok) throw new Error(`Ошибка ${response.status}`);
this.data = await response.json();
} catch (err) {
this.error = err.message;
} finally {
this.loading = false;
}
}
render() {
if (this.loading) return h('p', null, 'Загрузка данных...');
if (this.error) return h('p', { style: 'color: red;' }, `Ошибка: ${this.error}`);
return h('ul', null, this.data.map(item => h('li', null, item.name)));
}
}
define('data-loader', DataLoader);
В этом примере показана типичная структура для управления состояниями:
loading — логическое свойство, включаемое перед началом
асинхронной операции и отключаемое после завершения.error — строковое свойство для хранения сообщений об
ошибках.data — массив для хранения полученных данных.SkateJS автоматически отслеживает изменения свойств и вызывает метод
render. Это позволяет легко переключать отображение
компонента в зависимости от текущего состояния:
loading = true отображается индикатор
загрузки.error выводится сообщение об
ошибке.Такой подход обеспечивает декларативное управление состояниями без необходимости вручную манипулировать DOM.
Можно создавать более сложные индикаторы загрузки с анимацией или прогресс-баром. Пример с прогресс-баром:
render() {
if (this.loading) {
return h('div', { class: 'loader' },
h('div', { class: 'progress' })
);
}
if (this.error) {
return h('p', { class: 'error' }, `Ошибка: ${this.error}`);
}
return h('ul', null, this.data.map(item => h('li', null, item.name)));
}
Классы .loader и .progress могут быть
определены через CSS для отображения анимации. SkateJS оставляет полную
свободу в оформлении и позволяет интегрировать любые сторонние
библиотеки для визуализации загрузки.
В сложных компонентах часто требуется одновременно отслеживать несколько операций, каждая со своим состоянием. Для этого можно использовать объект состояния:
static props = {
state: props.object({ default: () => ({ loading: false, error: '', data: [] }) })
};
async fetchData() {
this.state = { ...this.state, loading: true, error: '' };
try {
const response = await fetch('https://api.example.com/items');
if (!response.ok) throw new Error(`Ошибка ${response.status}`);
this.state = { ...this.state, data: await response.json() };
} catch (err) {
this.state = { ...this.state, error: err.message };
} finally {
this.state = { ...this.state, loading: false };
}
}
render() {
const { loading, error, data } = this.state;
if (loading) return h('p', null, 'Загрузка данных...');
if (error) return h('p', { style: 'color: red;' }, `Ошибка: ${error}`);
return h('ul', null, data.map(item => h('li', null, item.name)));
}
Использование объекта state позволяет централизованно
управлять всеми состояниями компонента, особенно если их становится
несколько (например, loadingProfile,
loadingPosts, errorProfile,
errorPosts).
SkateJS позволяет динамически реагировать на изменения состояния без дополнительных событий. Например, можно отображать разные виды ошибок:
render() {
if (this.state.loading) return h('p', null, 'Загрузка...');
if (this.state.error) {
switch (this.state.error) {
case '404':
return h('p', { style: 'color: orange;' }, 'Данные не найдены.');
case '500':
return h('p', { style: 'color: red;' }, 'Серверная ошибка.');
default:
return h('p', { style: 'color: gray;' }, `Неизвестная ошибка: ${this.state.error}`);
}
}
return h('ul', null, this.state.data.map(item => h('li', null, item.name)));
}
Такой подход повышает гибкость компонентов и упрощает локализацию сообщений об ошибках.
Для повышения устойчивости компонентов можно внедрять логику повторной загрузки данных при ошибках:
async fetchData(retries = 3) {
this.state = { ...this.state, loading: true, error: '' };
for (let attempt = 1; attempt <= retries; attempt++) {
try {
const response = await fetch('https://api.example.com/items');
if (!response.ok) throw new Error(`Ошибка ${response.status}`);
this.state = { ...this.state, data: await response.json(), loading: false };
return;
} catch (err) {
if (attempt === retries) {
this.state = { ...this.state, error: err.message, loading: false };
}
}
}
}
Это позволяет компоненту быть более надежным при нестабильных сетевых условиях.
Можно связывать состояния загрузки и ошибок с событиями, например, кнопкой «Повторить»:
render() {
const { loading, error, data } = this.state;
if (loading) return h('p', null, 'Загрузка...');
if (error) {
return h('div', null,
h('p', { style: 'color: red;' }, `Ошибка: ${error}`),
h('button', { onclick: () => this.fetchData() }, 'Повторить')
);
}
return h('ul', null, data.map(item => h('li', null, item.name)));
}
Комбинация реактивных свойств и декларативного рендера обеспечивает простое управление пользовательским интерфейсом даже при сложной логике состояний.
SkateJS делает работу с состояниями загрузки и ошибок предсказуемой и реактивной, позволяя сосредоточиться на логике компонента и интерфейсе, а не на ручном управлении DOM. Использование свойств, объекта состояния и декларативного рендера обеспечивает масштабируемость и поддержку сложных сценариев асинхронных операций.