Репо находится здесь.
Коммит для этой части руководства находится здесь 🙂
Пора заняться настоящим кодингом. В этом посте мы завершим приложение 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
.