Микрофронтэнды Iframe: Автономное React-приложение

Репо находится здесь.
Коммит для этой части руководства находится здесь 🙂

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

Однако перед этим давайте настроим библиотеку models: перейдем в ./libs/models/src/lib, удалим файл spec, переименуем models.ts в activity-item.model.ts и обновим его содержимое интерфейсом ActivityItem, который соответствует сущности, возвращаемой Bored API, который мы будем использовать.

Да, мы используем ES4 всемогущий Typescript.

// ./libs/models/src/lib/activity-item.model.ts
export interface ActivityItem {
  activity: string;
  type: string;
  participants: number;
  price: number;
  link: string;
  key: string;
  accessibility: number;
}
Вход в полноэкранный режим Выход из полноэкранного режима

Пора создать наш React-компонент для отображения активности самым уродливым способом.

npm run nx -- g @nrwl/react:component activity --project=react-app --skipTests=true --export=false
Вход в полноэкранный режим Выход из полноэкранного режима

Переходим к нашему только что созданному компоненту, давайте немного почистим его и добавим логику для отображения Activity. Нет необходимости в интерфейсе props или экспорте по умолчанию, поэтому мы просто удалим их. Мы также удалим стили, в нашем приложении нет места для красоты. В итоге наш компонент должен выглядеть следующим образом:

// apps/react-app/src/app/activity/activity.tsx
import { useState } from 'react';

import { ActivityItem } from '@demo--nx-iframe-microfrontends/models';

export function Activity() {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  const [activity, setActivity] = useState<ActivityItem | null>(null);

  return (
    <div>
      <h3>Welcome to Activity!</h3>
      {activity &&
        Object.entries(activity).map(([k, v]) => (
          <p key={k}>
            <strong>{k}</strong>: {v}
          </p>
        ))}
    </div>
  );
}
Вход в полноэкранный режим Выход из полноэкранного режима

Не беспокойтесь о линтере, отключение комментария — это всего лишь временная мера.

Наша следующая цель — app.tsx, она будет изменена, поэтому служит только для перехода к нашему новому компоненту:

// ./apps/react-app/src/app/app.tsx
import { Navigate, Route, Routes } from 'react-router-dom';

import { Activity } from './activity/activity';

export function App() {
  return (
    <Routes>
      <Route path="/activity" element={<Activity />} />
      <Route path="*" element={<Navigate to="/activity" replace />} />
    </Routes>
  );
}
Вход в полноэкранный режим Выход из полноэкранного режима

Не забудьте обновить импорт App в main.tsx на именованный, поскольку мы удаляем стандартный. Все файлы spec и nx-welcome.tsx можно удалить, они не нужны для этого урока.

Теперь давайте создадим хук, который сможет предоставить нам функцию для запроса активности из Bored API. Конечно, мы могли бы импортировать функцию напрямую, но в будущем мы будем выполнять проверку iframe, и поэтому использование хука для импорта функции лучше в нашем случае: мы собираемся скрыть логику, откуда берется функция, поэтому сам компонент не будет знать, находится он внутри iframe или нет.

npm run nx -- g @nrwl/react:hook use-activity-provider --project=react-app --skipTests=true --export=false
Вход в полноэкранный режим Выход из полноэкранного режима

Итак, у нас есть хук, давайте подумаем об интерфейсе функции, которую он должен возвращать. Итак, у нас есть два случая:

  • приложение работает само по себе и запрашивает активность самостоятельно;
  • приложение запускается внутри iframe и просит своего родителя запросить активность.

Оба эти случая можно свести к интерфейсу функции, которая не требует аргументов и разрешается в обещание с ActivityItem, которое мы назовем GetActivity и поместим в ./apps/react-app/src/app/models/get-activity.model.ts:

// ./apps/react-app/src/app/models/get-activity.model.ts
import { ActivityItem } from '@demo--nx-iframe-microfrontends/models';

export interface GetActivity {
  (): Promise<ActivityItem>;
}
Вход в полноэкранный режим Выход из полноэкранного режима

Итак, теперь нам нужно реализовать служебную функцию, которая соответствует этому интерфейсу и будет использоваться при самостоятельном открытии приложения. Поместим ее внутри use-activity-provider, чтобы она была скрыта от остальной части приложения:

// apps/react-app/src/app/use-activity-provider/use-activity-provider.ts
import { ActivityItem } from '@demo--nx-iframe-microfrontends/models';

export async function fetchActivity(): Promise<ActivityItem> {
  const result = await fetch('http://www.boredapi.com/api/activity/');
  if (result.status === 200) {
    return result.json();
  }
  throw new Error('somethign went wrong');
}
Вход в полноэкранный режим Выход из полноэкранного режима

Довольно простое использование fetch. Наш хук провайдера готов предоставить его:

// apps/react-app/src/app/use-activity-provider/use-activity-provider.ts
import { GetActivity } from '../models/get-activity.model';
import { fetchActivity } from './fetch-activity.util';

export function useActivityProvider(): GetActivity {
  return fetchActivity;
}
Войти в полноэкранный режим Выход из полноэкранного режима

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

Наконец, мы можем вернуться к компоненту Activity и добавить некоторую логику для запроса и отображения активности самым уродливым способом:

// apps/react-app/src/app/activity/activity.tsx
import { useCallback, useState } from 'react';

import { ActivityItem } from '@demo--nx-iframe-microfrontends/models';

import { useActivityProvider } from '../use-activity-provider/use-activity-provider';

export function Activity() {
  const [activity, setActivity] = useState<ActivityItem | null>(null);
  const getActivity = useActivityProvider();
  const handleGetActivity = useCallback(
    () => getActivity().then(setActivity),
    [getActivity]
  );

  return (
    <div>
      <h3>Welcome to Activity!</h3>
      <button onClick={handleGetActivity}>get some activity!</button>
      {activity &&
        Object.entries(activity).map(([k, v]) => (
          <p key={k}>
            <strong>{k}</strong>: {v}
          </p>
        ))}
    </div>
  );
}
Вход в полноэкранный режим Выход из полноэкранного режима

Это уродливо и это работает, это все, что имеет значение, и это конец для этой части. В следующей части мы будем работать в приложении-оболочке Angular.

Оцените статью
Procodings.ru
Добавить комментарий