Skip to content

Instantly share code, notes, and snippets.

@builat
Created August 12, 2019 08:07
Show Gist options
  • Select an option

  • Save builat/c71c2d04cadc8f159ae363cc6fb113a5 to your computer and use it in GitHub Desktop.

Select an option

Save builat/c71c2d04cadc8f159ae363cc6fb113a5 to your computer and use it in GitHub Desktop.

Рекомендации по написанию React кода

Здравый смысл > рекомендации

Эти рекомендации не истина в последней инстанции и призваны упростить и систематизировать написание кода. Ни одна инструкция не описывает всего рабочего процесса целиком и в деталях.

Структура

  • Каждая фича на фронте представлена собственной папкой
  • В папке фичи собраны все связанные сущности, такие как Actions/ActionTypes/Saga/Reducer/Selector/Store/Typings/utils etc
  • В папке с фичёй должна содержаться папка с компонентами. Компонентами считать сущности реализующие JSX разметку. И не имеющие подключения к Store
  • Контейнеры должны лежать на одном уровне с папкой Components контейнеры, могут быть вложены друг в друга. Контейнером считать компонент, реализующий подключение к Store.
  • Файлы компонентов не должны быть слишком большими. В идеале компонент не должен занимать больше одного экрана. Но здравый смысл должен иметь место. Если невозможно сделать компонент меньше, грязно хакать и сокращать имена и прочее не нужно
  • С выходом React Hooks необходимость в классовых компонентах практически отпала. Поэтому стараемся использовать FC
  • Так как мы в новой версии используем styled-components логически можно разделить компоненты не только на stateless/statefull, но и styled. Если необходимо создать некий составной визуальный компонент и он отсутствует в UI-KIT то декларируем их в одном файле и экспортируем FC компонент, но не styled
  • Если некий параметр для styled-components используется повсеместно в компоненте, например isOpen/isToggled etc, то лучше вынести такое в отдельную функцию, тем самым избежав многократного создания лямбд на каждый рендер
  • Подключение ключей Store должно осуществляться только через селекторы
  • Если необходимо создать некий ключ в Store, который будет являться результатом преобразования другого ключа Store, то лучше делать его виртуальным, на базе селектора. Например есть ключ users, содержащий коллекцию пользователей, и нам необходим ключ adminUsers, содержащий только пользователей с правами администратора. То этот ключ должен быть реализован внутри селектора, как (isAdmin) => store.users.filter(({admin}) => admin === isAdmin)
  • Вся логика по поиску ключа store должна находиться внутри селектора
  • Если понадобился ключ из соседней фичи, лучше получать его импортом селектора из той же фичи
  • Т.к. мы используем immutable.js то необходимости в нормализации store фактически нет

Рекомендации по стилю

  const foo = { bar: 10 };
  const { bar } = foo; // Хорошо
  const foo = { bar: { baz: 10 } };
  const { bar: { baz } } = foo; // Плохо
  • Используйте reduce вместо последовательного применения .map().filter()
  const array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  const a = array.filter(e => e < 5).map(e => e * 2); // Плохо
  const b = array.reduce((acc, cur) => cur >= 5 ? [...acc, cur * 2] : acc, []); // Хорошо
  • Используйте reduce там где обычно используете циклы.
  • Чаще всего удобнее использовать маппинг с ключами внутри Map, чем switch или цепочку if
  const dict = new Map([ ['A', 10], ['B', 20], ['default', 0] ]);
  const f = (argument) => dict.has(argument) ? dict.get(argument) : dict.get('default');
  • Для того чтобы убедиться в наличии элемента в списке используйте some
  • Для того чтобы получить элемент возможно присутствующий в списке в единственном экземпляре, используйте find
  • Не пренебрегайте такими структурами как Map, Set
  • Такие библиотеки, как lodash или ramda предоставляют некоторые методы, уже появившиеся в стандартной спецификации языка. Такие как flatMap, flat, includes
  • Не мутируем данные. Переменные могут быть созданы, но не изменены.
  • Используем const везде где только можно. Избегаем let.
  • Для actions используем заранее задекларированные функции. Это позволит избежать многократной имплементации одинакового кода. DRY as IS
  • ВСЕГДА префиксируем ActionTypes следующим образом @@APP/FEATURE/SUBFEATURE/ACTION_TYPE__[SET|PUT|MOUNT|GET|FETCH] это нужно для того, чтобы ActionTypes из разных фичей никогда не пересекались
  • Активно пользуемся функциями высшего порядка, каррированием и композицией.
  • Перед тем как реализовывать какую-то функцию, посмотрите есть ли она в utility библиотеке, в нашем случае ramda. Если в utility библиотеке ничего подходящего не нашлось, посмотрите сначала в глобальном utils и если уж и там нет, то имплементируйте в локальном utils.ts в дальнейшем, можете перенести это в глобальный utils, если такая вероятность понадобится.
  • Если делаете словарь функций внутри объекта или Map имеет смысл делать функции именованными, в противном случае, когда что-то сломается, стэктрэйс будет очень сложно читать
  function some(a) { return a + 1 }
  function any(a) { return a - 1 }
  function identity(a) { return a }

  const badDict = new Map([ 
    ['add', (a) => a + 1],
    ['decr', (a) => a - 1], 
    ['default', (a) => a] 
  ]); // Плохо

  const goodDict = new Map([
    ['add', some],
    ['decr', any],
    ['default',identity]
  ]); // Хорошо
  • Если делаете словарь функций на основе Map, всегда реализуйте default ключ, к которому может свалиться запрос при провале. А вообще поищите в глобальном utils.ts, скорее всего конструктор словаря уже реализован там
  • Если в библиотеке специально не описано, что можно импортировать группой import {x,y,z} from 'foo' то импортируйте отдельные функции или компоненты import x from 'foo/x' хорошей идеей было бы использование специального плагина
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment