8. React
React — это JavaScript-библиотека с открытым исходным кодом, разработанная Facebook для создания пользовательских интерфейсов. Основная идея React — это построение интерфейса из небольших независимых компонентов, которые затем объединяются в сложные приложения. В этом разделе мы рассмотрим основы React и его практическое применение в проекте React-приложения медицинской клиники.
История и эволюция React

React был создан Джорданом Уолком, инженером Facebook, и впервые использовался для ленты новостей Facebook в 2011 году. Официально библиотека была выпущена с открытым исходным кодом в мае 2013 года. React является одной из самых популярных библиотек для создания пользовательских интерфейсов в мире, активно развивается и поддерживается сообществом разработчиков. React позволяет создавать пользовательские интерфейсы, которые могут быть использованы как на серверной, так и на клиентской стороне.
Ключевые вехи в истории React:
- 2013 — Первый публичный релиз React
- 2015 — Выпуск React Native для мобильной разработки
- 2016 — Выпуск React 15 с улучшенной поддержкой рендеринга
- 2017 — Полное переписывание ядра React (React Fiber)
- 2019 — Выпуск React 16.8 с хуками (Hooks)
- 2022 — Выпуск React 18 с конкурентным режимом рендеринга
С каждым новым релизом React развивается в направлении улучшенной производительности, упрощения API и предоставления более декларативных способов создания пользовательских интерфейсов.
Ключевые концепции React на примере React-приложения медицинской клиники
Рассмотрим основные концепции React на примере автоматически сгенерированного приложения медицинской клиники, которое демонстрирует современный подход к разработке с использованием TypeScript, GraphQL и Apollo Client.
1. Функциональные компоненты и TypeScript
React-приложение медицинской клиники построено исключительно на функциональных компонентах с использованием TypeScript для строгой типизации. Функциональные компоненты представляют собой JavaScript-функции, которые принимают объект props в качестве аргумента и возвращают JSX-элементы.
Преимущества функциональных компонентов:
- Простота и читаемость: Функциональные компоненты более лаконичны и легче для понимания
- Производительность: Меньше накладных расходов по сравнению с классовыми компонентами
- Хуки: Полная поддержка React Hooks для управления состоянием и побочными эффектами
- TypeScript интеграция: Отличная совместимость с системой типов TypeScript
В приложении медицинской клиники TypeScript обеспечивает безопасность типов на этапе компиляции, автодополнение в IDE и раннее обнаружение ошибок. Каждый компонент явно типизирован с помощью React.FC<Props>, что гарантирует корректность передаваемых параметров.
// src/App.tsx
export const App: React.FC = () => {
const [apolloClient, setAppoloClient] = useState<ApolloClient<NormalizedCacheObject>>()
const initClient = async () => {
if (process.env.NODE_ENV === 'production')
await initEnv()
if (!apolloClient) {
return new ApolloClient({
cache: cache,
uri: process.env.NODE_ENV === 'production' ? process.env.DS_ENDPOINT : '/graphql',
})
}
}
2. Хуки React для управления состоянием
Приложение активно использует хуки React для управления локальным состоянием компонентов. Хуки — это функции, которые позволяют "подцепиться" к возможностям React (состояние, контекст, побочные эффекты) из функциональных компонентов.
Основные хуки, используемые в приложении:
- useState: Управляет локальным состоянием компонента, возвращает текущее значение состояния и функцию для его обновления
- useEffect: Выполняет побочные эффекты (запросы к API, подписки, таймеры) и может очищать ресурсы при размонтировании компонента
- Пользовательские хуки: Apollo Client предоставляет специализированные хуки для работы с GraphQL запросами
Хуки обеспечивают:
- Переиспользование логики: Состояние и эффекты можно выносить в отдельные функции
- Предсказуемость: Хуки вызываются в одном и том же порядке при каждом рендере
- Простоту тестирования: Логику состояния легче изолировать и протестировать
В примере useState управляет состоянием Apollo Client, а useEffect инициализирует клиент при монтировании компонента.
// src/App.tsx
const [apolloClient, setAppoloClient] = useState<ApolloClient<NormalizedCacheObject>>()
useEffect(() => {
const appoloClientInit = async () => {
const apolloClient = await initClient()
setAppoloClient(apolloClient)
}
appoloClientInit()
}, [])
3. Интеграция с GraphQL через Apollo Client
Приложение демонстрирует современный подход к работе с API через GraphQL и Apollo Client. GraphQL — это язык запросов и среда выполнения для API, которая позволяет клиентам запрашивать именно те данные, которые им нужны. Apollo Client — это комплексная библиотека для управления данными, которая упрощает получение, кэширование и обновление данных в React-приложениях.
Преимущества GraphQL интеграции в приложении:
- Строгая типизация: Код-генерация создает TypeScript типы на основе GraphQL схемы
- Автоматические хуки: Каждый GraphQL запрос становится готовым к использованию React хуком
- Умное кэширование: Apollo Client автоматически кэширует результаты запросов и обновляет UI
- Оптимистичные обновления: Возможность обновлять UI до получения ответа от сервера
- Управление ошибками: Централизованная обработка сетевых ошибок и ошибок GraphQL
Процесс интеграции включает:
- Схема GraphQL определяет доступные операции и типы данных
- Код-генерация создает типизированные хуки для каждого запроса
- Apollo Client обеспечивает выполнение запросов и кэширование
- React компоненты используют сгенерированные хуки для получения данных
В примере useSearchDoctorQuery — это автоматически сгенерированный хук, который обеспечивает типобезопасный доступ к данным о врачах с поддержкой загрузки и обработки ошибок.
// src/components/__generate/Doctor/Doctor/DoctorList.tsx
import { useSearchDoctorQuery } from '../../../../__generate/graphql-frontend'
const { data, loading, error: queryError } = useSearchDoctorQuery(
{
variables: {
cond: (filterStr && filterStr !== 'undefined' && selectedDoctor !== null ) ? `it.${extractParamName(filterStr)}.id=='${extractParamValue(filterStr)}'` : null
}
})
4. Компонентная архитектура и композиция
Приложение следует принципам компонентной архитектуры, где сложные интерфейсы строятся из простых, переиспользуемых компонентов. Компонентная архитектура — это подход к разработке, при котором пользовательский интерфейс разбивается на независимые, переиспользуемые части, каждая из которых инкапсулирует свою логику и представление.
Ключевые принципы компонентной архитектуры в приложении:
- Единственная ответственность: Каждый компонент выполняет одну четко определенную задачу
- Переиспользование: Компоненты можно использовать в разных частях приложения
- Композиция над наследованием: Сложное поведение достигается комбинированием простых компонентов
- Инкапсуляция: Внутренняя логика компонента скрыта от внешнего мира
Иерархия компонентов в приложении:
- Layout компоненты: MainMenu, Header, Sider — определяют общую структуру
- Страничные компоненты: DoctorAgg, ClinicAgg — организуют логические разделы
- Функциональные компоненты: DoctorList, DoctorCreate — реализуют конкретную бизнес-логику
- UI компоненты: Button, Input, Table — базовые элементы интерфейса
React Router используется для декларативной навигации, где маршруты определяются как компоненты и могут быть вложенными. Это обеспечивает:
- Декларативную маршрутизацию: Структура URL отражает иерархию компонентов
- Ленивую загрузку: Компоненты загружаются только при необходимости
- Программную навигацию: Возможность навигации через JavaScript код
В примере MainMenu демонстрирует композицию Layout, Menu и Routes компонентов для создания навигационной структуры приложения.
// src/components/__generate/MainMenu.tsx
export const MainMenu: FC = () => {
return (
<BrowserRouter>
<Layout>
<Header>
<Menu mode='horizontal' >
<Menu.Item key='DoctorAgg'>
<Link to={'/DoctorAgg'}>DoctorAgg</Link>
</Menu.Item>
// ... другие пункты меню
</Menu>
</Header>
<Routes>
<Route key={"Home"} path="/" element={<Home />}>
<Route key={"DoctorAgg"} path="DoctorAgg" element={<DoctorAgg />}>
{DoctorAggRouteList()}
</Route>
</Route>
</Routes>
</Layout>
</BrowserRouter>
)
}
5. Управление формами с контролируемыми компонентами
Формы в приложении реализованы как контролируемые компоненты, где состояние формы управляется через props и обратные вызовы, обеспечивая предсказуемое поведение. Контролируемые компоненты — это паттерн в React, при котором значения элементов формы хранятся в состоянии React компонента и обновляются через обработчики событий.
Принципы работы контролируемых компонентов:
- Единственный источник истины: Состояние формы хранится в React состоянии
- Контролируемый поток данных: Все изменения проходят через React обработчики
- Валидация в реальном времени: Можно проверять данные при каждом изменении
- Предсказуемость: Состояние формы всегда синхронизировано с UI
Преимущества контролируемых форм:
- Полный контроль: Возможность программно изменять значения полей
- Валидация: Проверка данных на каждом шаге ввода
- Условная логика: Динамическое показ/скрытие полей на основе других значений
- Интеграция с состоянием: Легкая интеграция с глобальным состоянием приложения
Архитектура форм в приложении:
- Родительский компонент (DoctorCreate) управляет состоянием всей формы
- Форма-компонент (DoctorCreateForm) получает данные через props и отправляет изменения через callback
- Отдельные поля связаны с конкретными свойствами состояния через value и onChange
Паттерн обратного вызова changeInputParameters обеспечивает обновление состояния в родительском компоненте при изменении любого поля формы. Это создает надежный поток данных и упрощает валидацию и сохранение данных.
// src/components/__generate/Doctor/Doctor/DoctorCreateForm.tsx
export const DoctorCreateForm: FC<FormProps> = ({ inputParameters, changeInputParameters }) => {
return (
<Form>
<Form.Item label="doctorType">
<Space>
<Input
value={inputParameters.doctorType}
onChange={e => changeInputParameters({ doctorType: e.target.value})}
/>
<Button onClick={() => setSelectedDoctorTypeId(null)}>{"..."}</Button>
</Space>
</Form.Item>
<Form.Item label="id">
<Input
value={inputParameters.id!}
onChange={e => changeInputParameters({ id: e.target.value })}
/>
</Form.Item>
</Form>
)
}
6. Декларативный рендеринг и условная отрисовка
React позволяет декларативно описывать пользовательский интерфейс. Декларативный подход означает, что разработчик описывает что должно быть отображено в зависимости от текущего состояния, а не как манипулировать DOM для достижения желаемого результата. Это противоположность императивному подходу, где необходимо явно указывать каждый шаг изменения интерфейса.
Принципы декларативного рендеринга:
- Состояние как единственный источник истины: UI является функцией от состояния приложения
- Автоматическое обновление: React автоматически обновляет DOM при изменении состояния
- Предсказуемость: Одинаковое состояние всегда приводит к одинаковому UI
- Простота отладки: Легко понять, какое состояние привело к текущему отображению
Паттерны условной отрисовки в приложении:
- Условный рендер по состоянию загрузки: Показ спиннера во время запроса данных
- Обработка ошибок: Отображение компонента ошибки при неудачном запросе
- Условные элементы UI: Показ кнопок и элементов в зависимости от контекста
- Списки с фильтрацией: Динамическое отображение элементов на основе условий
Преимущества декларативного подхода:
- Читаемость: Код явно выражает намерения разработчика
- Надежность: Меньше возможностей для ошибок при манипуляции DOM
- Производительность: React оптимизирует обновления DOM
- Тестируемость: Легко тестировать различные состояния компонента
В примере показана последовательная проверка состояний: сначала loading, затем error, и наконец успешное отображение данных. Это типичный паттерн для асинхронных операций в React приложениях.
// src/components/__generate/Doctor/Doctor/DoctorList.tsx
if (loading) return (<Spin tip="Loading..." />);
if (queryError) {
return (<ErrorModal error={queryError} setError={setError} />)
} else {
client.refetchQueries({include:["searchDoctor"]})
return (
<Space direction='vertical'>
{selectedDoctor === undefined && <Button
onClick={() => {
navigate(`/DoctorAgg/Doctor/Create/${filterStr}`)
}}
>
Create
</Button>}
<Table
columns={columns}
dataSource={mapToView(elemList)}
onRow={(record) => ({ onClick: event => { if (setSelectedDoctor) setSelectedDoctor(record.id) } })}
/>
</Space>
)
}
7. Паттерн Provider для глобального состояния
Приложение использует паттерн Provider для предоставления Apollo Client всем дочерним компонентам, что обеспечивает доступ к GraphQL API на любом уровне вложенности. Паттерн Provider основан на React Context API и решает проблему "prop drilling" — необходимости передавать данные через множество промежуточных компонентов, которые сами эти данные не используют.
Ключевые концепции паттерна Provider:
- Context API: Встроенный механизм React для передачи данных через дерево компонентов
- Provider компонент: Предоставляет значение всем дочерним компонентам
- Consumer или хуки: Позволяют дочерним компонентам получить доступ к значению
- Избежание prop drilling: Данные доступны на любом уровне вложенности без промежуточной передачи
Архитектура Provider в приложении:
- ApolloProvider оборачивает все приложение и предоставляет Apollo Client
- Дочерние компоненты получают доступ к клиенту через Apollo хуки
- Автоматическое управление: Apollo автоматически управляет подпиской на обновления
Преимущества использования Provider:
- Глобальная доступность: Данные доступны в любом компоненте приложения
- Централизованное управление: Одно место для управления глобальным состоянием
- Производительность: React оптимизирует ре-рендеры только для подписанных компонентов
- Типобезопасность: TypeScript обеспечивает типизацию контекста
Альтернативы паттерну Provider:
- Prop drilling: Передача данных через props (подходит для простых случаев)
- Внешние библиотеки состояния: Redux, Zustand, Valtio для сложной логики состояния
- Серверное состояние: Apollo Client, React Query для данных с сервера
В примере ApolloProvider делает GraphQL клиент доступным для всех компонентов, что позволяет им использовать хуки типа useQuery, useMutation без необходимости явной передачи клиента через props.
// src/App.tsx
if (apolloClient)
return (
<ApolloProvider client={apolloClient}>
<MainMenu />
</ApolloProvider>
)
return (<>{"Loading..."}</>)
8. Обработка событий и навигация
Современные React-приложения активно используют программную навигацию через хуки React Router. Обработчики событий передаются как функции и вызываются при взаимодействии пользователя. Обработка событий в React следует принципу синтетических событий, которые обеспечивают кроссбраузерную совместимость и дополнительные возможности.
Принципы обработки событий в React:
- Синтетические события: React оборачивает нативные события для единообразного поведения
- Функции как значения: Обработчики передаются как функции в JSX атрибутах
- Автоматическое связывание: В функциональных компонентах контекст this не требуется
- Предотвращение по умолчанию: Использование
preventDefault()для отмены стандартного поведения
Типы событий в приложении медицинской клиники:
- Click события: Обработка кликов по кнопкам, строкам таблицы, ссылкам
- Change события: Отслеживание изменений в полях ввода форм
- Submit события: Обработка отправки форм
- Navigation события: Программная навигация между страницами
Навигация с React Router:
- useNavigate хук: Программная навигация через JavaScript
- Link компоненты: Декларативная навигация через JSX
- Параметры маршрута: Передача данных через URL параметры
- Query параметры: Дополнительная информация в строке запроса
Паттерны навигации в приложении:
- Условная навигация: Переход только при выполнении определенных условий
- Навигация с параметрами: Передача ID сущности через URL
- Программная навигация: Автоматический переход после успешных операций
- Навигация с состоянием: Передача дополнных данных через
state
Преимущества такого подхода:
- Предсказуемость: Ясная связь между действиями пользователя и поведением приложения
- Отладка: Легко отследить поток событий и навигации
- Доступность: Поддержка клавиатурной навигации и скрин-ридеров
- SEO: Корректные URL для поисковых систем
В примере показано, как клик по строке таблицы может вызвать callback для выбора элемента, а useNavigate позволяет программно перейти к форме создания с передачей контекстных параметров.
// src/components/__generate/Doctor/Doctor/DoctorList.tsx
const navigate = useNavigate();
return (
<Table
columns={columns}
dataSource={mapToView(elemList)}
onRow={(record) => ({
onClick: event => {
if (setSelectedDoctor) setSelectedDoctor(record.id)
}
})}
/>
)
Эти примеры демонстрируют, как современные концепции React применяются в реальном приложении, обеспечивая масштабируемость, типобезопасность и удобство разработки.
Ant Design

Ant Design — это enterprise-класса UI библиотека для React, разработанная компанией Ant Financial. В проекте медицинской клиники используется Ant Design версии 4.13.0 в качестве основной UI библиотеки, что обеспечивает профессиональный внешний вид и богатую функциональность компонентов.
Основные преимущества Ant Design
Готовые компоненты высокого качества: Ant Design предоставляет более 50 готовых компонентов, покрывающих все потребности корпоративных приложений — от простых кнопок до сложных таблиц с сортировкой и фильтрацией.
Единый дизайн-язык: Все компонены следуют единым принципам дизайна, что обеспечивает визуальную согласованность интерфейса без дополнительных усилий от разработчика.
TypeScript поддержка: Библиотека написана на TypeScript и предоставляет полную типизацию для всех компонентов и их API.
Интернационализация: Встроенная поддержка множества языков и локализаций, включая русский язык.
Применение в приложении медицинской клиники
1. Layout и навигация
В приложении активно используются компоненты Layout и Menu для создания общей структуры интерфейса:
// src/components/__generate/MainMenu.tsx
import { Layout, Menu } from 'antd';
const { Header } = Layout;
export const MainMenu: FC = () => {
return (
<BrowserRouter>
<Layout>
<Header>
<Menu mode='horizontal' >
<Menu.Item key='DoctorAgg'>
<Link to={'/DoctorAgg'}>DoctorAgg</Link>
</Menu.Item>
// ... другие пункты меню
</Menu>
</Header>
// ... маршруты
</Layout>
</BrowserRouter>
)
}
2. Формы и элементы ввода
Компоненты Form, Input, Button, Space и Modal используются для создания сложных форм ввода данных:
// src/components/__generate/Doctor/Doctor/DoctorCreateForm.tsx
import { DatePicker, Checkbox, Form, Input, Select, Space, Button, Modal } from 'antd';
export const DoctorCreateForm: FC<FormProps> = ({ inputParameters, changeInputParameters }) => {
return (
<>
<Form>
<Form.Item label="doctorType">
<Space>
<Input
value={inputParameters.doctorType}
onChange={e => changeInputParameters({ doctorType: e.target.value})}
/>
<Button onClick={() => setSelectedDoctorTypeId(null)}>{"..."}</Button>
</Space>
</Form.Item>
</Form>
<Modal
visible={selectedDoctorTypeId === null}
footer={null}
width="90%"
onCancel={() => setSelectedDoctorTypeId(undefined)}
>
<DoctorTypeList selectedDoctorType={selectedDoctorTypeId} />
</Modal>
</>
)
}
3. Отображение данных и индикаторы состояния
Для отображения списков данных и индикации состояния загрузки используются Table, Spin и другие компоненты:
// src/components/__generate/Doctor/Doctor/DoctorList.tsx
import { Button, Space, Spin, Table } from 'antd';
if (loading) return (<Spin tip="Loading..." />);
return (
<Space direction='vertical'>
<Button onClick={() => navigate(`/DoctorAgg/Doctor/Create/${filterStr}`)}>
Create
</Button>
<Table
columns={columns}
dataSource={mapToView(elemList)}
onRow={(record) => ({ onClick: event => { setSelectedDoctor(record.id) } })}
/>
</Space>
)
4. Модальные окна и обработка ошибок
Modal компонент используется для отображения ошибок и диалоговых окон:
// src/components/basic/ErrorModal.tsx
import { Button, Modal } from 'antd';
const ErrorModal: React.FC<Props> = ({ error, setError }) => {
return (
<Modal footer={null} visible={true} onCancel={() => setError(null)}>
{error.message}
</Modal>
)
};
Интеграция с проектом
Подключение стилей
В главном файле приложения подключаются стили Ant Design:
// src/App.tsx
import 'antd/dist/antd.css';
Зависимости проекта
В package.json указаны необходимые зависимости:
antd: "4.13.0"— основная библиотека компонентов@ant-design/icons: "^5.1.4"— набор иконок для интерфейса
Архитектурные решения
Консистентность интерфейса: Использование единой UI библиотеки обеспечивает согласованный пользовательский опыт по всему приложению.
Быстрая разработка: Готовые компоненты значительно ускоряют процесс создания интерфейса, особенно в контексте автоматической генерации кода.
Адаптивность: Компоненты Ant Design поддерживают адаптивный дизайн и корректно отображаются на различных устройствах.
Доступность: Библиотека следует стандартам WAI-ARIA, обеспечивая доступность для пользователей с ограниченными возможностями.
Ant Design в проекте медицинской клиники выступает как надежная основа для создания профессионального корпоративного интерфейса, позволяя сосредоточиться на бизнес-логике вместо создания базовых UI компонентов.
Заключение
Проект React-приложения медицинской клиники демонстрирует эффективный подход к автоматической генерации типобезопасного React-приложения на основе GraphQL-схемы. Используя принципы кодогенерации, проект значительно ускоряет разработку и обеспечивает согласованность кода.
Сгенерированное приложение демонстрирует современные практики разработки с использованием TypeScript и React:
- Строгая типизация всех компонентов и данных
- Функциональное программирование с использованием хуков
- Компонентная архитектура с четким разделением ответственности
- Декларативный подход к построению пользовательского интерфейса
- Интеграция с GraphQL API через Apollo Client
Такой подход позволяет сосредоточиться на бизнес-логике приложения, вместо написания типовых компонентов и запросов вручную. В следующем разделе учебного курса мы рассмотрим интеграцию механизмов авторизации и расширение функциональности проекта с использованием AI-инструментов.
Ссылки
