Что включает:
- 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 - язык программирования от 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>
- Как понять какой тип указывать? Нужно искать тип там, где собираемся вызывать функцию или компонент. Навести мышкой на переменные, которые будем передавать, и увидим их тип.
В качестве библиотеки компонентов предлагается использовать 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. Их можно использовать как по отдельности так и комбинировать.
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 - библиотека для управления состоянием. Она фреймворк-агностик, но в основном используется в react. Является стандартом де-факто, хотя сами создатели пишут, что ощутимую пользу она дает только 10% проектов. Ее характеристики:
- По смыслу похожа на контекст, по усложнена дополнительными сущностями
- Больше бойлерплейта нужно писать
- Более быстрая засчет structure sharing
- Хорошие девтулы
- Архитектурно продумана, поэтому имеет плюшки в виде middlewares, например
В шаблоне используются вспомогательные инструменты для redux - redux toolkit. Поэтому основная документация здесь. Они сильно упрощают работут с redux, потому что чистый redux неудобоварим.
Как использовать?:
- С redux скорее всего вы уже знакомы, поэтому пример в
src/state/current-user
Раздел для разработки с комфортом). Прежде чем начинать что либо писать, нужно собрать команду и обсудить проект взять своего бэкендера и обсудить взаимодействие фронта и бэка, дефинировать необходимые ручки, лучше всего на бумаге). То есть попросите своего бэкендера сделать 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 не имеет лоадера, поэтому подумайте как в него можно это добавить
- А вот кидать сообщения об неудачных запросах он умеет
В проекте есть линтер (eslint) он умеет находить проблемы, поэтому поставьте в редактор расширение для него, чтобы видеть ворнинги. Прогнать через eslint весь проекст и посмотреть ворнинги можно через npm run lint
.
Также есть prettier для форматирования кода. Поставьте расширение в редактор и активируйте опцию formatOnSave. Прогнать через prettier весь проект можно через npm run format
.
Чего нет в шаблоне:
- Темизации. Скорее всего вы ее захотите, чтобы проект мог выделиться. Читаем тут
- Роутера. Он тоже может понадобиться в некоторых темах. Если вы понимаете, что вам нужен разные url в браузере, то берем react-router
- WebSocket. Его можно очень легко использовать с react-query. Или обойтись обычным поллингом через опцию refetchInterval (пример в
src/components/chat/ChatSpace.tsx
)