Atomico — современная библиотека для создания веб-компонентов с реактивной архитектурой, вдохновлённой концепциями React. Одной из ключевых возможностей является управление асинхронными данными с использованием паттернов, похожих на React Suspense. Эти подходы позволяют компонента эффективно обрабатывать загрузку данных и состояние ожидания без излишнего усложнения логики рендеринга.
useResource для асинхронных данныхВ Atomico базовым инструментом для работы с асинхронными ресурсами
является хук useResource. Он позволяет описывать источник
данных и автоматически управлять его состоянием: загрузкой, ошибкой и
успешным результатом.
import { component, useResource, html } from "atomico";
async function fetchUser(id) {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
if (!response.ok) throw new Error("Ошибка загрузки данных");
return response.json();
}
function UserComponent({ id }) {
const user = useResource(() => fetchUser(id), [id]);
return html`
<div>
${user.loading && html`<p>Загрузка...</p>`}
${user.error && html`<p>Ошибка: ${user.error.message}</p>`}
${user.value && html`<p>Имя пользователя: ${user.value.name}</p>`}
</div>
`;
}
export const User = component(UserComponent);
Ключевые моменты:
useResource автоматически отслеживает зависимости
([id]) и повторно запускает загрузку при их изменении.user.loading, user.error и
user.value позволяют управлять различными состояниями
асинхронного запроса без отдельного state-хука.Atomico поддерживает ленивую загрузку компонентов с помощью динамического импорта. Этот подход тесно связан с Suspense-подобной концепцией — компонент отображается только после загрузки всех необходимых ресурсов.
import { lazy, component, html } from "atomico";
const LazyProfile = lazy(() => import("./ProfileComponent.js"));
function App() {
return html`
<div>
<h1>Пример ленивой загрузки</h1>
<${LazyProfile} />
</div>
`;
}
export const MainApp = component(App);
Особенности ленивой загрузки:
lazy возвращает компонент-заглушку, который рендерится
после завершения импорта.useResource для управления
загрузкой данных внутри ленивого компонента.SuspenseAtomico предоставляет возможность создания собственных обёрток для
асинхронного рендеринга, аналогичных Suspense. Основная
идея — использовать реактивный state для отслеживания загрузки ресурсов
и отображать fallback-контент до завершения загрузки.
import { component, html, useState, useEffect } from "atomico";
function Suspense({ fallback, children }) {
const [loaded, setLoaded] = useState(false);
useEffect(() => {
Promise.resolve(children.props.resource).then(() => setLoaded(true));
}, [children.props.resource]);
return html`${loaded ? children : fallback}`;
}
function DataDisplay({ resource }) {
return html`<p>Данные: ${resource.value}</p>`;
}
// Пример использования
function Example() {
const resource = useResource(() => fetchUser(1), []);
return html`
<${Suspense} fallback=${html`<p>Загрузка...</p>`} resource=${resource}>
<${DataDisplay} resource=${resource} />
<//>
`;
}
export const Demo = component(Example);
Выделенные идеи:
Suspense отделяет состояние ожидания от
основной логики рендеринга.Promise.resolve, чтобы унифицировать
работу с синхронными и асинхронными ресурсами.При работе с Suspense-подобными паттернами важно контролировать
повторные запросы данных. Atomico предоставляет useMemo и
возможность кэширования ресурсов.
import { useResource, useMemo, component, html } from "atomico";
function CachedUser({ id }) {
const resource = useMemo(() => useResource(() => fetchUser(id), [id]), [id]);
return html`
<div>
${resource.loading && html`<p>Загрузка...</p>`}
${resource.value && html`<p>Имя: ${resource.value.name}</p>`}
</div>
`;
}
export const UserCached = component(CachedUser);
Atomico позволяет строить иерархию компонентов с асинхронными зависимостями, где каждый компонент загружает свои данные и рендерится только после завершения загрузки:
function ParentComponent() {
const posts = useResource(() => fetch("/posts").then(res => res.json()), []);
return html`
<div>
${posts.loading && html`<p>Загрузка постов...</p>`}
${posts.value &&
posts.value.map(
post => html`<${ChildComponent} postId=${post.id} />`
)}
</div>
`;
}
function ChildComponent({ postId }) {
const comments = useResource(() => fetch(`/posts/${postId}/comments`).then(r => r.json()), [postId]);
return html`
<div>
${comments.loading && html`<p>Загрузка комментариев...</p>`}
${comments.value &&
comments.value.map(comment => html`<p>${comment.body}</p>`)}
</div>
`;
}
Суть каскадного паттерна:
Suspense-подобные паттерны в Atomico позволяют создавать реактивные интерфейсы, где асинхронные данные интегрированы напрямую в поток рендеринга, минимизируя boilerplate и обеспечивая гибкость управления состояниями загрузки и ошибок.