Реализуйте на TypeScript хук `usePage`, который принимает URL, загружает данные пользователя, возвращает `data`, `loading` и `error`, повторяет запрос при изменении URL и корректно отменяет запрос при размонтировании компонента или смене URL.

Хук должен связывать ответ с текущим URL, очищать предыдущий запрос и отдельно возвращать загрузку и ошибку.

Короткий ответ

Что ответить на собеседовании

Хук должен связывать ответ с текущим URL, очищать предыдущий запрос и отдельно возвращать загрузку и ошибку.

Подробный разбор

Ответ с пояснениями

Хук должен связывать ответ с текущим URL, очищать предыдущий запрос и отдельно возвращать загрузку и ошибку.

Пример ожидает пользователя с id и name:

import { useEffect, useState } from 'react';
type User = { id: string | number; name: string };
type PageState = { data: User | null; loading: boolean; error: string | null };
export function usePage(url: string): PageState {
  const [saved, setSaved] = useState<{ url: string; state: PageState }>({
    url, state: { data: null, loading: true, error: null }
  });
  useEffect(() => {
    const controller = new AbortController();
    let active = true;
    setSaved({ url, state: { data: null, loading: true, error: null } });
    async function load() {
      try {
        const response = await fetch(url, { signal: controller.signal });
        if (!response.ok) throw new Error('Ошибка загрузки');
        const value: unknown = await response.json();
        if (!value || typeof value !== 'object' || !('id' in value) ||
          !('name' in value) || typeof value.name !== 'string' ||
          (typeof value.id !== 'number' && typeof value.id !== 'string')) {
          throw new Error('Некорректный ответ');
        }
        const data: User = { id: value.id, name: value.name };
        if (active) setSaved({ url, state: { data, loading: false, error: null } });
      } catch (error) {
        if (active && !controller.signal.aborted) setSaved({ url, state: {
          data: null, loading: false,
          error: error instanceof Error ? error.message : 'Ошибка загрузки'
        } });
      }
    }
    load();
    return () => { active = false; controller.abort(); };
  }, [url]);
  return saved.url === url ? saved.state : { data: null, loading: true, error: null };
}

Проверка URL не позволяет показать прежнего пользователя даже в рендере перед запуском нового эффекта.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее