Skip to content

Latest commit

 

History

History

frontend-template

Шаблон для курсовой работы ИУ5 по сетям (frontend)

Что включает:

  • Create React App
  • TS
  • React Material UI
  • Styled Components (Emotion)
  • Генерация API по сваггеру
  • Сервер для мокирования бэкенда (Mirage JS)
  • Redux, Redux Toolkit
  • React Context
  • Кеширование, рефетч, реконнект, инвалидацию запросов, optimistic updates (React Query)
  • Тосты (React Toastify)
  • Prettier, format imports, eslint
  • Анимации (React Animations)

Далее подробнее про шаблон.

TypeScript

TypeScript - язык программирования от Microsoft, надмножество JavaScript, то есть имеет с ним полную совместимость. Проще говоря JavaScript + типы.

Основное для чего нужен TS - предоставление контрактов. Ни одно сколько-нибудь серьезное приложение не может обойтись без контрактов, потому что на них строится вся бизнес-логика и предметная область. Плюшки в виде обнаружения типичных ошибок, автокомплит это приятный бонус.

В общем случае контракты != типы. Например, строка с хешированным паролем должна быть 32 символа в длину, в типе можно указать только то, что это строка.

Тут интро в ts.
Здесь можно посмотреть как использовать ts вместе с react.

Как использовать?:

  • В основном требуется просто указывать тип аргументов функции, пропсов компонента
  • В случае функции, которая принимает позиционные аргументы, то можно указывать тип прямо на месте:
const sum = (a: number, b: number) => a + b
  • В случае функции, которая принимает объект в качестве аргумента (например, react компонент) удобнее вынести тип в переменную:
type Props = {
  somePropA: string;
  somePropB: number;
}

export const Component = (props: Props) => return <div></div>
  • Как понять какой тип указывать? Нужно искать тип там, где собираемся вызывать функцию или компонент. Навести мышкой на переменные, которые будем передавать, и увидим их тип.

UI

Библиотека компонентов

В качестве библиотеки компонентов предлагается использовать React Material UI, достаточно популярная, по набору компонентов хороший среднячок. Библиотека реализует Google Material Design, который можно активно видеть в андроиде.

Из альтернатив можно использовать React Bootstrap, она поскронее. Или Ant Design, она помощнее.

Как использовать?:

  • Заходим в раздел компонентов в документации и изучаем все компоненты, чтобы потом выбирать под задачу.
  • Например, кнопка, видим, что компонент подходит под наши фронтовые цели, берем пример использования из документации и вставляем в код
  • Смотрим что получается, немного туним пропсами, которые принимает компонент
  • Достуные пропсы можно посмотреть в самом конце страницы компонента, в разделе API

Стили

Для стилей предлагается использовать CSS-in-JS. Пожалуй, самый популярный и подходящий вариант для написания стилей в react. Конкретную реализацую данного подхода в шаблоне берет библиотека emotion, потому что react mui использует ее. По ней мало информации, но ее api очень похож на api библиотеки styled components, прародительницы подхода CSS-in-JS. Поэтому искать инфу можно по ключевому слову styled components.

Некоторые плюсы данного подхода:

  • Типизация стилей
  • Мощь JavaScript в стилях
  • Переход к стилям по кнопке
  • Ликвидация мертвого CSS
  • Все js: и логика, и шаблон, и стили
  • Нет загромождения className
  • Легко писать сложные компоненты с кучей состояний (размеры, цвета)

Как использовать?:

  • пишем мы div и понимаем, что хотим добавить стилей к нему, тогда:
const Container = styled.div`
    display: flex;
    padding: 10px;
    background: white;
    overflow: hidden;
`;

вот так описываем стилизованный div, на выходе получаем компонент и уже используем его

  • если мы хотим добавить стилей к существующему компоненту, то делаем так:
const StyledSendIcon = styled(SendIcon)`
    cursor: pointer;
    color: blue;
    animation: 0.3s ${fadeInRightAnimation};
`;

SendIcon в данной случае уже существующий компонент

  • еще в стили можно передавать пропсы, посмотреть можно здесь

Анимации

Для анимация добавлена простенькая библиотека react animations - простенькая обертка над animate.css. Пример использования можно найти в src/components/chat/ChatMessageInput.tsx.

Для чего-нибудь помощнее можно добавить framer.
Или использовать нативный Web Animations API, который дает много контроля над анимацией.

Тосты

Для оповещения пользователя добавлены нотификации, которые реализуются через react-toastify. В любом месте приложения можно вызвать toast.error('your error') и уведомить пользователя об ошибке.

Работа с клиентским состоянием

Для работы с клиентским состоянием предлагается два варианта: react context и redux. Их можно использовать как по отдельности так и комбинировать.

Context

React Context - встроенная в реакт функциональность, позволяющая избегать пропс дриллинга, то есть прокидывать пропсы вглубину сквозь компоненты.

Про контекст читаем тут.
Про контекст в функциональных компонентах тут.

Как использовать?:

  • В шаблоне в src/context/active-chat.tsx показан пример создания контекста
React.createContext({
    activeChat: null,
    setActiveChat: () => {},
});

Вот так можно создать контекст, в котором будет храниться активный/выбранный чат. Добавив setActiveChat (функцию изменения состояния), из контекста можно сделать хранилище состояния.

  • В одном контексте не стоит хранить разные состояния, лучше их дробить
  • Небольшая удобная оберточка над контекстом:
const ActiveChatProvider: React.FC<{ children: ReactNode }> = ({
    children,
}) => {
    const [activeChat, setActiveChat] = useState<Chat | null>(null);

    return (
        <ActiveChatContext.Provider value={{ activeChat, setActiveChat }}>
            {children}
        </ActiveChatContext.Provider>
    );
};

Чтобы передать контекст мы должны использовать его Provider. в value мы передаем уже конкретное значение контекста. Здесь мы используем useState для хранения и обновления состояния.

  • Дальше эту обертку мы используем в App.tsx:
<ActiveChatProvider>
    <Root />
</ActiveChatProvider>
  • А во вложенных компонентах получать значение контекста можем так:
const { activeChat } = useContext(ActiveChatContext);

Redux

Redux - библиотека для управления состоянием. Она фреймворк-агностик, но в основном используется в react. Является стандартом де-факто, хотя сами создатели пишут, что ощутимую пользу она дает только 10% проектов. Ее характеристики:

  • По смыслу похожа на контекст, по усложнена дополнительными сущностями
  • Больше бойлерплейта нужно писать
  • Более быстрая засчет structure sharing
  • Хорошие девтулы
  • Архитектурно продумана, поэтому имеет плюшки в виде middlewares, например

В шаблоне используются вспомогательные инструменты для redux - redux toolkit. Поэтому основная документация здесь. Они сильно упрощают работут с redux, потому что чистый redux неудобоварим.

Как использовать?:

  • С redux скорее всего вы уже знакомы, поэтому пример в src/state/current-user

Генерация API

Раздел для разработки с комфортом). Прежде чем начинать что либо писать, нужно собрать команду и обсудить проект взять своего бэкендера и обсудить взаимодействие фронта и бэка, дефинировать необходимые ручки, лучше всего на бумаге). То есть попросите своего бэкендера сделать swagger документацию и держать ее в актульном состоянии. Ему это почти бесплатно, а польза всем: фронтендер сможет всегда видеть актуальные ручки и их параметры, а бэкендера не будут постоянно дергать.

Как использовать?:

  • В шаблоне пример документации находится в spec.yml
  • По нему можно сгенерировать api командой node api-codegen.js
  • Сгенерированный api будет находиться в src/api/generated
  • Если ваша спецификация будет не в spec.yml, а например в json или доступна по ссылке, то нужно изменить input в файле api-codegen.js
  • После генерации появятся сервисы и их можно использовать, например так:
await ChatSerive.getChats()
  • Также сразу появятся типы сущностей (Chat), которые можно использовать в TS

Мокирование запросов к бэкенду

Cгенерированный api есть, а бэкенда еще нет, как будем разрабатываться? Можно в целом подождать, а можно начать параллельно. Например, накидывать интерфейс с уже заполненным стором на клиенте. А если уже хочется добавлять логику работы с сервером, то можно воспользоваться miragejs. Это инструмент, который перехватывает браузерный fetch и отдает свои мокированные данные.

Как использовать?:

  • Запустить сервер разработки с этой фичей можно командой npm run start:stub-server. Она просто ставит переменную окружения, по которой активируется miragejs, и запускает npm run start
  • Исследовать файлик src/server.js, в нем находятся все моки, и документацию, благо она микроскопическая
  • C miragejs мы по сути описываем свой маленький бэкенд, но очень примитивно и быстро
  • Определяем ручки, которые будет перехватывать miragejs
  • Для get запросов ничего сложного, просто создаем фейковые объекты и возвращаем их из ручки
  • Простые post, put, delete тоже без проблем, для этого нужно будет немного логики написать
  • Miragejs умеет и в отношения (one to one, many to many), правда здесь нужно будет немного повникать

Работа с состоянием сервера

Работа с состоянием сервера важная часть современных приложений. Сейчас приложения очень интерактивные, часто обновляются и при этом имеют состояние, которое хранится удаленно (сервер). Поэтому важно уметь с этим работать, не изобретая велосипедов. Для этого будем использовать библиотеку react-query. По набору функциональности она самая лучшая.

Что дает?:

  • Кеширование
  • Retry, по умолчанию равный 3м и экспоненциальный
  • Перезапрос данных по смещению фокуса (например, между вкладками), по маунту компонента, по возобновлению связи (например, если был disconnect)
  • Инвалидация запросов. Так как каждый запрос маркируется через queryKey, его лего потом инвалидировать в любом месте приложения
  • Отмена запросов
  • Поллинг
  • Optimistic Updates. Когда мы обновлем интерфейс до завершения запроса
  • Пагинаця, бесконечный скролл

Как использовать?:

  • Для получения данных есть небольшая обертка над react-query - FetchWrapper в src/components/common/FetchWrapper.tsx
  • Простейший пример получения всех чатов:
<FetchWrapper<Chat[]>
    queryKey="chats"
    fetchFn={async () => await ChatService.getChats()}
    render={({ data }) => {
        return <ChatList chats={data} />;
    }}
/>
  • queryKey - уникальный ключ запроса. Строка или массив, лучше использовать массив.
  • fetchFn - функция, которая делает запрос, то есть получает данные
  • render - сюда передаем компонент, который отрендерится после получения данных, data непосредственно данные с сервера
  • в emptyEl можно передать компонент, который рендерится во время первонального получения данных, какая-нибудь заглушка, например, чтобы не было сдвигов макета
  • теперь когда нам нужно получить заново чаты, например, после создания нового, можно использовать:
qc.invalidateQueries('chats');

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

  • FetchWrapper не имеет лоадера, поэтому подумайте как в него можно это добавить
  • А вот кидать сообщения об неудачных запросах он умеет

Misc

В проекте есть линтер (eslint) он умеет находить проблемы, поэтому поставьте в редактор расширение для него, чтобы видеть ворнинги. Прогнать через eslint весь проекст и посмотреть ворнинги можно через npm run lint.

Также есть prettier для форматирования кода. Поставьте расширение в редактор и активируйте опцию formatOnSave. Прогнать через prettier весь проект можно через npm run format.

Чего нет в шаблоне:

  • Темизации. Скорее всего вы ее захотите, чтобы проект мог выделиться. Читаем тут
  • Роутера. Он тоже может понадобиться в некоторых темах. Если вы понимаете, что вам нужен разные url в браузере, то берем react-router
  • WebSocket. Его можно очень легко использовать с react-query. Или обойтись обычным поллингом через опцию refetchInterval (пример в src/components/chat/ChatSpace.tsx)