awesome-everything EN
↑ Обратно к восхождению

Архитектура фронтенда

Data fetching: чтение кода

Суть Чтение реальных сниппетов fetching — серверный waterfall, queryKey, RSC-разметка с Suspense и гонка поиска — выбери поведение или фикс с наибольшим рычагом.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min

Баги data fetching читаются в коде и в панели Network. Прочитай каждый сниппет, предскажи форму round-trip’ов и выбери фикс, который senior-фронтендер делает первым.

Цель

Потренируй цикл, который запускаешь на каждом ревью fetching: проследи, какие запросы последовательны vs параллельны, прочитай ключ кэша как идентичность, заметь RSC-waterfall, прячущийся в цикле, и убей гонку ответов в источнике.

Сниппет 1 — цикл в серверном компоненте

// Server Component — рендерит список из 50 заказов
async function OrderList({ ids }: { ids: string[] }) {
  const orders = [];
  for (const id of ids) {
    orders.push(await fetchOrder(id));   // один await на итерацию
  }
  return <ul>{orders.map(o => <Order key={o.id} data={o} />)}</ul>;
}
Викторина

Каждый fetchOrder занимает ~40ms. Каково суммарное время fetch и каков фикс?

Сниппет 2 — ключ запроса

function useProduct(id: string, currency: string) {
  return useQuery({
    queryKey: ['product', id],                 // currency НЕ в ключе
    queryFn: () => fetchProduct(id, currency),
  });
}
Викторина

Пользователь переключает валюту с USD на EUR на том же товаре. Что ломается и почему?

Сниппет 3 — RSC-разметка

// Server Component страница
async function ProductPage({ id }) {
  const product = await fetchProduct(id);        // быстро, ~50ms
  return (
    <main>
      <h1>{product.name}</h1>
      <Suspense fallback={<ReviewsSkeleton />}>
        <Reviews productId={id} />               {/* медленно, ~800ms */}
      </Suspense>
    </main>
  );
}
Викторина

Reviews занимает 800ms. Когда пользователь впервые видит заголовок товара и почему?

Сниппет 4 — поле поиска

function useSearch(query: string) {
  const [results, setResults] = useState([]);
  useEffect(() => {
    fetch(`/api/search?q=${query}`)
      .then(r => r.json())
      .then(setResults);                  // нет cleanup, нет отмены
  }, [query]);
  return results;
}
Викторина

При быстром наборе это периодически показывает результаты более раннего запроса. Каков минимальный корректный фикс?

Итог

Каждый баг fetching виден в коде: await внутри цикла — это серверный waterfall, Promise.all ограничивает его самым медленным вызовом; queryKey должен содержать каждый вход, меняющий ответ, иначе кэш отдаёт неправильную запись; Suspense-граница позволяет оболочке стримиться на TTFB, пока медленные дети разрешаются независимо; а неотменённый fetch в эффекте гоняется на быстром вводе — AbortController в cleanup или библиотека, отменяющая устаревшие ключи, дают победу последнему запросу. Читай код, проследи round-trip’ы, чини структуру прежде чем тянуться к настройке.

Продолжить восхождение ↑Data fetching: схлопни critical path
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.