Эти рекомендации не истина в последней инстанции и призваны упростить и систематизировать написание кода. Ни одна инструкция не описывает всего рабочего процесса целиком и в деталях.
- Каждая фича на фронте представлена собственной папкой
- В папке фичи собраны все связанные сущности, такие как 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' хорошей идеей было бы использование специального плагина