Впервые я познакомился с 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 в связке, они уже стали можно сказать стандартами для разработки.

Комментарии (3)


  1. alex_pkzdl
    24.10.2022 13:55

    Неплохое овервью, спасибо! Можешь рассказать про ccs in js поподробнее, какие библиотеки приходилось использовать в проектах и почему?


  1. extrany
    24.10.2022 13:55

    Это то что мне давно не хватало!


  1. kagi
    24.10.2022 14:38

    Интересно, спасибо.