Впервые я познакомился с React в 2015 году и вот уже использую его можно сказать повседневно 7 лет. Бесчисленное количество компонентов было написано за это время, React из подающей надежды модной технологии вырос в серьезную библиотеку и по сути стал стандартом для написания веб приложений в 2022 году.
Мы полюбили эту библиотеку за простое и лаконичное, но в тоже время очень мощное API, производительность, крутейшее коммюнити, наличия множество npm пакетов и просто за возможность решать прикладные задачи быстро и легко.
Скриншот официального сайта https://reactjs.org
Много споров в интернете, про то называть React библиотекой или фреймворков, но на официальном сайте написано библиотека, наверное им виднее.
В целом я согласен, поскольку React достаточно гибкий и в целом никак не диктует разработчикам как именно писать приложения, оставляя простор для воображения или для других разработчиков.
Решил собрать несколько советов, которые за эти годы доказали свою эффективность и масштабируемость уже не в одном проекте.
Примеры будут расположены не по важности, а скорее просто в рандомном порядке в котором я про них вспомнил, наверняка многие из них вы уже используете в своей кодовой базе, по скольку я не уверен что хотя бы один из этих паттернов я придумал сам, а не подсмотрел у других разработчиков, но так или иначе, это не отменяет их эффективности.
Design system driven development
Да этот совет не совсем про React, а скорее про организацию разработки и взаимодействия с дизайнерами. Если у вас есть возможность инвестировать время в e2e дизайн систему от Figma/Sketch макетов до React компонентов, это действительно помогает экономить время при решении бизнес задач с развитием проекта.
Единая цветовая палитра/шрифты, вертикальный ритм, компоненты и модули, с одинаковым неймингом позволят вам очень быстро переносить интерфейсы с макетов в код. Кстати ребята из Figma даже запалили плагин, который при корректной настройке сможет прямо в макете подсказывать какой компонент с какими props нужно использовать разработчику для реализации этого интерфейса
Ссылочка тут.
Чтобы подытожить, тема 10/10 чем раньше на стадии жизненного цикла проекта начнете тем легче и лучше будет.
KISS
Пытаться сохранять компоненты простыми это очень важно для их дальнейшей поддержки, масштабирования и переиспользования.
Безусловно есть случаи, когда сложность логики не позволяет сохранить API компонента простым и чистым, но нужно пытаться декомпозировать на переиспользуемые более простые слои и абстрагировать “сложный” код в одну из них.
Этот подход например очень важен при создании дизайн системы, нужно соблюсти грамотный баланс между гибкостью и строгостью API ваших компонентов, чтобы они могли скейлиться, но в то же время вы не теряли контроль.
Тут нету золотого стандарта, потому что строгость и гибкость в данном случае обратные понятия и вам нужно будет решить самостоятельно исходя из размера команды, зрелости проекта и прочих условий.
import React from 'react'
export default function UILibInputSelect(props) {
const {
dataTestID,
className,
children,
onClick,
} = props;
return (
<UILibInputSelect onClick= {onClick} dataTestID={dataTestID} className={className}>
{children}
</UILibInputSelect>
);
}
UILibInputSelect.useValue = useValue
const useValue = ({ initialItems }) => {
const [items, setItems] = React.useState(initialItems);
const getCheckedItems = (items) => items.filter(({ checked }) => checked);
const onChange = React.useCallback(
(id: string) => setItems(items.map(item => ({...item,checked: id === item.id}))),
[items],
);
const checkedItems = React.useMemo(() => getCheckedItems(items), [items]);
const onReset = React.useCallback(() => setItems(initialItems), [initialItems]);
return useMemo(
() => ({ items, onChange, onReset, checkedItems }),
[checkedItems, items, onChange, onReset,],
);
};
UI Agnostic components
В IOS, Android и многих других нативных клиентах, платформа предоставляет разработчик достаточно высокоуровневые абстракции компонентов, которые уже решают за вас проблемы унификации низкоуровневых интерфейсов, accessibility, производительность и сложные UX приемы..
К сожалению в Web разработке у нас такого нет, некоторые html теги вполне себе являются этой абстракцией, но к сожалению они более низкоуровневые и их не много.
Если вы меняли команды/проекты/компании работая во фронтоне последние лет 5 вы замечали, что приходя в новую команду, часто вам приходится создавать те же самые компоненты, как и в прошлой, только в новой теме. Так вот чтобы решить эту. Проблему и сэкономить время, можно использовать UI Agnostic или Headless компоненты.
Идея этого подхода заключается как раз в том, чтобы иметь готовый набор решений, на которые можно добавить абстракцию темы и получить готовую дизайн систему со знакомым интерфейсом и уже реализованным низкоуровневым функционалом, который так долго и сложно каждый раз изобретать снова.
Альтернативой тут могут служить уже готовые библиотеки компонентов вроде MUI, Bootstrap, AntDesign и многих других, но не все продуктовые команды будут готовы пожертвовать гибкость. Headless компоненты дают больше свободы разработчикам,, но и ответственности тоже становится больше.
Примеры библиотек куда посмотреть: HeadlessUI, BaseWeb и другие
Binary doesn’t scale
Component props в React это по сути маленький уровень API компонента, с которым другие разработчики будут взаимодействовать при его переиспользовании, по сути это одна из самых важных и ответственных частей в написании компонент. Так вот кроме упомянутого выше KISS, которого нужно придерживаться здесь еще один полезный совет, старайтесь избегать булевых флагов в интерфейсах если логически интерфейс пусть даже и не прямо сейчас, но в будущем может иметь третье и более значение, используйте Enum, String Union.
import React from 'react'
export default function UILibInputSelect(props) {
const {
variant,
dataTestID,
className,
children,
onClick,
} = props;
switch (variant) {
case UILibInputSelectVariant.small:
return (
<UILibInputSelectSmall onClick= { onClick } dataTestID = { dataTestID } className = { className }>
<UILibInputSelectSmallFlyout>
{children}
<UILibInputSelectSmallHint />
</UILibInputSelectSmallFlyout>
</UILibInputSelect>
);
default:
case UILibInputSelectVariant.default:
return (
<UILibInputSelect onClick= { onClick } dataTestID = { dataTestID } className = { className } >
{ children }
< /UILibInputSelect>
);
}
}
UILibInputSelect.useValue = useValue
UILibInputSelect.variant = UILibInputSelectVariant
React Component as Namespace
Этот совет плавно вытекает из предыдущего, чтобы не заставлять разработчика думать откуда импортировать значение props-ов, зная, что в js все — объект, можно положить сами значения внутрь компонента в статичные свойства под теми же именами что и props-ы и переиспользовать их вместе с компонентом.
Этот небольшой совет помогает ускорить разработку и сохранить консистентность кода в команде, со временем все разработчики привыкнут, что запрашиваемые статичные типы параметров уже предоставлены наборов в используемом компоненте, в купе со статической типизацией это позволит сократить время поиска корректных параметров.
import React from 'react'
const App = () => {
return <div>
<UILibInputSelect variant={UILibInputSelect.variant.small} />
</div>
}
CSS in JS
Если ваш проект вам позволяет, используйте css in js решения, они решают множество проблем классического css или css modules, а производительность их уже хороша. Самое популярное решение здесь это styled-components. У них непривычный синтаксис, но к этому привыкаешь и тебя перестает тоншить, а бенефиты остаются
Плюсы:
- Возможность задешево передавать динамическое значение в стили без оверхед.
- Типизация.
- Dead Code Elimination.
- Хорошо подходит для реализации дизайн систем.
Минусы:
- Ниже производительность.
- Синтаксис.
- Не подходит для анимаций.
Строгая статическая типизация
В 2022 году если вы не разрабатываете проект с очень коротким жизненным циклом, то использование языка со строгой статической типизацией уже must have.
Самым популярным выбором конечно же является Typescript. Быстрый, гибкий, хорошо интегрируемый с JS, React экосистемой и средами разработка, имеющий широкое признание в сообществе — отличный выбор.
Кроме того, что вы получите дополнительный слой безопасности для вашего приложения, вы еще и лишитесь постоянной мороки с поддержанием пачки Babel плагинов, получите более минималистичный конфиг и меньше мороки по настройке.
Также связка Typescript со средой программирования поможет ускорить разработку за счет умных подсказок для параметров функций, props-ов компонентов и прочего.
Автоформатирование кода и линтинг
К сожалению ни JavaScript ни TypeScript не имеют встроенного функционала по автоформатированию кода, но я настоятельно рекомендую озаботиться этими проблемами в начале проекта.
Это поможет вам не только поддерживать кодовую базу в едином стилистике, но и уменьшит число конфликтов внутри команды на этой почве. Также наличие единой стилистики написания кода ускоряет разработку в целом, человеческий мозг любит повторяющиеся паттерны, они усваиваются проще и быстрее.
Используйте Prettier и ES/TS-lint в связке, они уже стали можно сказать стандартами для разработки.
alex_pkzdl
Неплохое овервью, спасибо! Можешь рассказать про ccs in js поподробнее, какие библиотеки приходилось использовать в проектах и почему?