Загрузка данных: createResource и Suspense
Ресурс — это сигнал для асинхронных данных: он сам вызывает функцию загрузки, хранит результат и сообщает, идёт ли загрузка и не случилась ли ошибка. Так Solid загружает данные с сервера без ручных эффектов и флагов.
С этого урока пример меняется на маленький блог. Создайте новый проект так же, как список задач:
npx degit solidjs/templates/js blog
cd blog
npm install
npm run devСтатьи будут приходить с тестового API https://jsonplaceholder.typicode.com.
createResource
Создайте компонент src/components/PostList.jsx:
// src/components/PostList.jsx
import { createResource, For, Show } from 'solid-js';
const fetchPosts = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5');
return res.json();
};
export default function PostList() {
const [posts] = createResource(fetchPosts);
return (
<Show when={!posts.loading} fallback={<p>Загрузка...</p>}>
<ul>
<For each={posts()}>{(post) => <li>{post.title}</li>}</For>
</ul>
</Show>
);
}Выведите <PostList /> в src/App.jsx. Сначала на http://localhost:3000 появится «Загрузка...», затем список из пяти заголовков статей.
Что есть у ресурса
| Свойство | Значение |
|---|---|
posts() | Загруженные данные или undefined |
posts.loading | true, пока идёт запрос |
posts.error | Ошибка, если запрос не удался |
Второй элемент пары — объект с функциями refetch (загрузить заново) и mutate (заменить данные вручную): const [posts, { refetch }] = createResource(fetchPosts).
Ресурс с параметром
Если первым аргументом передать сигнал, ресурс будет перезагружаться при каждом его изменении:
// src/components/PostView.jsx
import { createSignal, createResource, Show } from 'solid-js';
const fetchPost = async (id) => {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
return res.json();
};
export default function PostView() {
const [id, setId] = createSignal(1);
const [post] = createResource(id, fetchPost);
return (
<div>
<button onClick={() => setId(id() + 1)}>Следующая статья</button>
<Show when={post()}>{(p) => <h2>{p().title}</h2>}</Show>
</div>
);
}Каждое нажатие загружает следующую статью и показывает её заголовок. Значение сигнала приходит в fetchPost первым аргументом.
Suspense
Suspense показывает запасное содержимое, пока не загрузятся все ресурсы, прочитанные внутри него:
// src/App.jsx
import { Suspense } from 'solid-js';
import PostList from './components/PostList';
export default function App() {
return (
<Suspense fallback={<p>Загрузка блога...</p>}>
<PostList />
</Suspense>
);
}Так можно убрать Show из PostList и описать загрузку один раз на всю страницу.
Частая ошибка
Загружать данные в createEffect или onMount и складывать их в сигнал вручную. Это работает, но приходится самим заводить флаги загрузки и ошибки, а Suspense о таком запросе ничего не узнает. Для данных с сервера используйте createResource.