Асинхронные данные

MDX — это расширение Markdown, позволяющее использовать JSX-компоненты внутри Markdown-документов. Одной из ключевых возможностей MDX является интеграция динамического контента, включая асинхронные данные, что открывает широкие возможности для построения интерактивных документов и приложений.

Использование асинхронных функций внутри MDX

В стандартном Markdown невозможно выполнять асинхронные операции, однако MDX позволяет включать JSX, а значит, любые компоненты React, включая те, что загружают данные из API. Для работы с асинхронными данными чаще всего применяются React-хуки, такие как useEffect и useState.

Пример асинхронного компонента внутри MDX:

import { useState, useEffect } from 'react';

function AsyncDataComponent() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchData() {
      try {
        const response = await fetch('https://api.example.com/items');
        const result = await response.json();
        setData(result);
      } catch (error) {
        console.error('Ошибка при загрузке данных:', error);
      } finally {
        setLoading(false);
      }
    }
    fetchData();
  }, []);

  if (loading) return <p>Загрузка данных...</p>;
  if (!data) return <p>Данные не найдены</p>;

  return (
    <ul>
      {data.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

<AsyncDataComponent />

В этом примере MDX выступает как контейнер для React-компонента, который выполняет асинхронный запрос и отображает результат. Ключевые моменты:

  • useEffect с пустым массивом зависимостей [] гарантирует, что запрос выполнится один раз при монтировании компонента.
  • useState используется для хранения данных и состояния загрузки.
  • Асинхронная функция fetchData обрабатывает ошибки через try/catch и корректно обновляет состояние компонента.

Асинхронные данные и серверный рендеринг (SSR)

MDX активно применяется в системах статической генерации сайтов, таких как Next.js. При использовании MDX вместе с SSR возникает задача получения данных на сервере до рендеринга страницы. В этом случае асинхронные данные можно загружать через функции getStaticProps или getServerSideProps.

Пример:

// pages/posts/[id].js
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
import { MDXRemote } from 'next-mdx-remote';
import { serialize } from 'next-mdx-remote/serialize';

export default function PostPage({ mdxSource, frontMatter }) {
  return (
    <article>
      <h1>{frontMatter.title}</h1>
      <MDXRemote {...mdxSource} />
    </article>
  );
}

export async function getStaticProps({ params }) {
  const filePath = path.join(process.cwd(), 'posts', `${params.id}.mdx`);
  const source = fs.readFileSync(filePath, 'utf8');
  const { content, data } = matter(source);

  const mdxSource = await serialize(content);

  return { props: { mdxSource, frontMatter: data } };
}

Особенности:

  • serialize из next-mdx-remote позволяет подготовить MDX-документ для рендеринга на сервере.
  • Асинхронные операции, такие как чтение файлов или вызов внешнего API, выполняются до передачи данных компоненту.
  • Компоненты внутри MDX могут использовать переданные пропсы для отображения динамического контента.

Загрузка данных через React Server Components (RSC)

Современные версии React позволяют использовать серверные компоненты, которые могут асинхронно загружать данные на сервере. В MDX это особенно удобно для больших проектов с интенсивным API-взаимодействием.

Пример серверного компонента в MDX:

// components/ServerData.jsx
export default async function ServerData() {
  const res = await fetch('https://api.example.com/posts');
  const posts = await res.json();

  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

Использование в MDX:

<ServerData />

Преимущества:

  • Данные загружаются на сервере, а клиент получает уже готовый HTML.
  • Отсутствует необходимость в useEffect и состоянии для загрузки данных на клиенте.
  • Снижает количество запросов с клиента и улучшает производительность.

Управление состоянием загрузки и ошибок

При работе с асинхронными данными важно обрабатывать стадии загрузки и ошибки, чтобы пользователь видел корректную информацию.

Структура компонентов для асинхронных данных может включать:

  1. loading — флаг загрузки данных.
  2. error — объект ошибки.
  3. data — сам контент.

Пример интеграции с MDX:

function DataLoader({ fetcher }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetcher()
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [fetcher]);

  if (loading) return <p>Загрузка...</p>;
  if (error) return <p>Ошибка: {error.message}</p>;
  return <pre>{JSON.stringify(data, null, 2)}</pre>;
}

// Использование
<DataLoader fetcher={() => fetch('/api/data').then(res => res.json())} />

Интеграция с внешними API и библиотеками

MDX-компоненты позволяют подключать любые библиотеки для работы с асинхронными данными:

  • SWR — React-хук для оптимизированного кэширования и повторного запроса.
  • React Query / TanStack Query — управление состоянием асинхронных запросов с кэшированием, предзагрузкой и синхронизацией.

Пример использования SWR в MDX:

import useSWR from 'swr';

function SWRData() {
  const { data, error } = useSWR('/api/items', url => fetch(url).then(res => res.json()));

  if (error) return <p>Ошибка загрузки</p>;
  if (!data) return <p>Загрузка...</p>;

  return (
    <ul>
      {data.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

<SWRData />

Такой подход минимизирует повторные запросы и обеспечивает более отзывчивый интерфейс, особенно при интеграции MDX в сложные приложения.

Важные практики при работе с асинхронными данными в MDX

  • Разделение логики и представления: вынесение асинхронных операций в отдельные компоненты упрощает поддержку и тестирование.
  • Обработка ошибок и состояния загрузки: даже при динамическом контенте страницы должны оставаться информативными.
  • Серверная предварительная загрузка данных: для SEO и производительности лучше отдавать HTML уже с контентом, если это возможно.
  • Кэширование и повторное использование данных: использование библиотек вроде SWR или React Query снижает нагрузку на сервер и ускоряет интерфейс.

Асинхронные данные в MDX открывают возможности создания документов и приложений, которые могут обновляться в реальном времени, работать с API и интегрироваться с современными фреймворками React, сохраняя при этом удобство написания контента в формате Markdown.