Pull to refresh
36
0

Frontend developer (React, MobX)

Send message

Принцип единой ответственности (SRP)

Каждый модуль и функция имеют четко определенную ответственность. Например, ReactFiberBeginWork.js фокусируется на начальной фазе процесса согласования, а ReactFiberCompleteWork.js обрабатывает завершающую фазу.

Компонент отвечает и за логику и за отображение. Нарушение SRP.

Принцип открытости/закрытости (OCP)

Библиотека открыта для расширения, но закрыта для модификации. Это видно по использованию хуков, которые позволяют добавлять новую функциональность без изменения основных компонентов React.

Также они демонстрируют нарушение этого принципа, ибо без изменения кода компонента его поведение нельзя расширить/изменить добавлением в него новых хуков или их заменой.

Для добавления объекту новой функциональности есть 3 подхода - смешивание с другим объектом, обертка другим объектом, замена объектов/функций в полях-ссылках объекта на другие объекты/функции.

Вариации 3-го подхода:
Можно вынести метод в функцию вне класса и вызывать ее в соответствующем методе жизненного цикла в нужных компонентах.

Можно завести в компонентах массив ссылок на подобные функции, тем самым еще и позволяя менять функциональность компонента, чего нет в подходе с хуками.

Можно пойти дальше и сделать ссылки не на функции, а на объекты, в которых сделать методы жизненного цикла, аналогичные компонентам. Далее в каждом методе жизненного цикла сделать перебор объектов с вызовом в них соответствующего метода жизненного цикла. Еще понадобиться предоставить этим объектам доступ к компоненту с его состоянием. Тем самым получим механизм, где обработка логики компонентов реакта будет делегирована вложенным переиспользуемым и заменяемым объектам. Плюс верстка будет отделена от логики.

Реализацию такого подхода я описывал в https://habr.com/ru/articles/545064

<skz>Вот же этот Ден Абрамов, стервец, дурачит газилионы и газилиарды разработчиков!</skz>

Пишу на реакте с 2015-го. И что-то не ощущаю профита в разработке от стольких лет его эволюции. Скорее наоборот, разработка на нем стала сложнее и медленнее(

под fsd вы имеете ввиду использования feature-list вместо layer-list структуры топлевел директорий

Под fsd я имею ввиду то же, что и автор статьи - https://feature-sliced.design/ru/
Вы верно подметили про layer-list - что неудобно распределять фичу по топлевел директориям. FSD как раз грешит этим.

Про MV* не совсем понял. Это же паттерны презентации, а не архитектура ...

Я тоже не совсем понял, что именно вы имеете ввиду под архитектурой?) Ок, именно они - не архитектура, а паттерны, используемые для разбиения приложения на слои в многослойной архитектуре.
Но я согласен, что эти слои должны находиться рядом для конкретного виджета/страницы.

Люди слишком много думают, а на деле фсд очень даже простой)

Судя по множеству вопросов и разногласий даже среди senior разрабов в том, что и где должно лежать, фсд на самом деле довольно сложный)

Даже выше в комментах было:

"Подход имеет место быть, но стоит держать в уме, что просто взять и перенести средний/большой проект на fsd не выйдет без специалиста, который хорошо знаком с данным подходом. Сталкивался с ситуацией когда на проекте fsd и всё круто, но в какой-то момент людям приходит понимание, что большая часть разработчиков распихивает код по слоям рандомно и проект превращается в кашу."

Всё это разделение на множество слоев ради переиспользования без кросс импортов скорее приводит не к упрощению, а к усложнению. Плюс функционал, связанный с одной бизнес сущностью, становиться разбросан по нескольким слоям проекта. Думаю, в большинстве проектов было бы эффективней не делить на слои widgets, features, entities, а объединить эти 3 слоя в один слой и разрешить в нем кросс импорты между слайсами.

Тоже добавлю про fsd. Решили его попробовать на текущем среднем проекте - несколько десятков тысяч строк кода.

Плюсы:

  • Много документации по подходу.

  • Хорошая идея с разделением на слайсы, сегменты.

  • Знакомишься со сторонними идеями по улучшению файловой структуры проекта.

Минусы:

  • В ходе работы возникает путаница, когда разговор заходит о слоях. В архитектурах MV*, Flux и т.д. слоями называется другое.

  • При разработке возникает много непонятных моментов - какие слои нужны именно в твоем случае (а при избегания кросс-доменности складывается впечатление, что понадобятся все), к какому слою относится тот или иной функционал (у всех вызывает сложность, когда надо определиться, данный модуль это feature или entity? Также возникают сложности с определением, куда поместить такой функционал, как локализация, темизация, переиспользуемые пагинация/сортировка/фильтрация). Все в какой-то степени по-разному понимают FSD, поэтому в команде скорее всего будут частые обсуждение, что и где должно лежать.

  • Логика страницы вместо того, чтобы находиться рядом в соседних файлах/папках, постоянно размазывается по 3-4 папкам - pages, widgets, features, entities. Это повышает когнитивную нагрузку. Часто приходиться переключаться между далеко расположенными друг от друга папками. На мой взгляд, структурировать проект, беря декомпозицию компонентов за значительную часть основы - это не удачный подход.

  • Тратиться немало времени на поддержку следования данному подходу. Если бизнесу постоянно нужны фичи и не выделяется достаточно временно на техдолг, то времени заниматься поддержкой FSD в проекте не будет.

  • Сложно контролировать на code review, т.к. сходу часто не понятно, в какой слой правильней поместить новый модуль.

В следующем проекте вместо слоев FSD, я подумываю использовать примерно следующие:

  1. app - по аналогии с FSD

  2. pages - конкретные страницы и всё (включая конкретные api), относящееся только к конкретной страницы. (вместо постоянного разделения на pages, widgets, features, entities по FSD)

  3. reusable-in-pages - переиспользуемый функционал для нескольких страниц. (вместо widgets, features, entities по FSD)

  4. components - переиспользуемые визуальные компоненты, а также react хуки/улилиты и т.д., относящиеся к этим компонентам.


    В двух следующих слоях визуальные компоненты отсутствуют, но компоненты-провайдеры могут присутствовать в 5 слое, т.к. они не визуальные.

    Хотя возможно 4-ый и 5-ый слои лучше объединить в один, т.к. некоторый функционал (например, toasts) связан с компонентами. А возможно shared стоит использовать как в FSD. Т.е. 4-6 слои из моего комментария сделать одним слоем.

  5. shared-business - переиспользуемый функционал, не относящаяся к конкретным страницам или к какой-либо группе страниц, но привязанная к работе, процессам именно этого приложения. (вместо shared/libs по FSD)

  6. shared-not-business - всё прочее переиспользуемое, не привязанное к приложению, и которое в теории может быть переиспользовано в любом другом проекте. В основном это типы, утилиты, некоторые react-hook-и. (по аналогии с FSD, но без shared/ui и без shared/libs)

однако если углубиться в механизмы при помощи которых подобная замена реализуема в классовых компонентах - это либо наследование, либо мутация.

Реализация классовых компонентов у команды реакта получилась так себе. К более менее правильному подходу на классах они так и не пришли. Я как раз и описывал подход без наследования и мутаций, похожий на хуки, который они могли бы сделать вместо своей реализации.

Про то, что хуки это исключительно стратегия не соглашусь

Я попытался показать, что механизмы расширения функционала при помощи паттерна стратегия и при помощи хуков довольно похожи. Абстрагируйтесь, не зацикливайтесь на ограничениях. Тогда станет заметно, что механизмы, применимые к функциям, можно применить к объектам, и наоборот.

Единственным способом переиспользования классовых методов жизненного цикла - является как раз написание HOC’ов.

Я писал статью про другие способы, которые при желании можно применить и к классовым компонентам реакта: Техники повторного использования кода и разбиения сложных объектов на составные

HOC - это аналог паттерна "декоратор" для функциональных компонентов.

Хук - это аналог паттерна "стратегия" для функциональных компонентов. Только у функциональных компонентов недостаток в том, что в них нельзя заменить хук (стратегию), а в объектах/классах при использовании паттерна "стратегия", можно.

Намертво прибиты к реакту - тоже выдуманный пункт, классовые методы тоже прибиты к реакту, точнее даже к конкретному компоненту.

Классовые методы можно выносить в отдельные объект и переиспользовать в других компонентах. Просто из коробки нет общего подхода для такого переиспользования.

Касательно хуков. Группа хуков, вызов которых прописан в конкретном функциональном компоненте - это фиксированный список "стратегий". В геймдеве давно есть похожий подход, но применяемый к объектам/классам. Но там используется не фиксированный список, а динамический. Плюс более грамотное разделение ответственности. В реакте же компонент является и контейнером для стратегий (хуков), содержит в себе разметку, а также позволяет писать логику в самих компонентах. Такое объединение функционала является недостатком как функциональных, так и в классовых компонентах. Это нарушение принципа SRP, приводящее к уменьшение гибкости компонента и менее качественному коду пользователей react-а.

Недавно наткнулся на видео на ту же тему, что и ваша статья
https://www.youtube.com/watch?v=c3JGBdxfYcU
У меня вопросы:

  1. С каких пор во фронтенде архитектурой стали называть организацию структуры папок в проекте?

  2. "Components, Store, Containers", "Feature Oriented" - откуда взяты такие названия для подходов?

В Redux используется паттерн издатель-подписчик. В вашей статье описан тоже он.

Паттерн наблюдатель немного другой. По ссылке описана разница между ними:
https://medium.com/clean-code-channel/observer-vs-pub-sub-pattern-4fc1da35d11

useEffect(() => {
  (async () => {
    const {data} = await axios.get('api/posts')
    setPosts(data)
  })()
}, [])

Я бы предпочел избавиться от такого количества скобок и писать например так:

useMountEffectAsync(async () => {
  const {data} = await axios.get('api/posts')
  setPosts(data)
})

Про оптимизацию я глупость написал, так как ни разу не приходилось использовать requestAnimationFrame.

После прочтения фраз вроде "не перегружать CPU бесполезными вычислениями" в голове почему-то засела мысль, что вы ее возможно для каких-то оптимизаций используете.

Думаю, BuccapuoH, как и я, хотел узнать, что у вас за проект такой, в котором понадобилось столько раз использовать requestAnimationFrame и process.nextTick) И была ли в этом необходимость или же это просто преждевременная оптимизация.

Стандартные подходы Vue, где во Vuex экшенах делаются запросы и кладутся в стор и т.п не расширяемые

Мне тоже всегда виделось, что это плохо масштабируется. Поэтому вынес из стора обращения к сервисам в отдельный слой, похожий на описанный у вас презентер. Правда на React пишу, но они с Vue довольно похожи.

Принципы SOLID конечно помогают, но одних их довольно маловато. Существует довольно много паттернов, подходов, позволяющих улучшить переиспользование кода. Я, кстати, писал статью о тех, с которыми знаком: https://habr.com/ru/post/545368/

Видимо я ошибался. Теперь начинаю видеть преимущества подхода, особенно в плане оптимизации. А недостаток в виде раздувания FormItem обходится с помощью вынесения логики из него в хуки, сторы. Спасибо, что открыли мне глаза!)

На своей же практике встретил неудачное применение клонирование компонентов только ради вынесения общих пропсов, что создавало нам проблемы. Подумал, что и в статье то же самое, но оказалось, что нет.

С формиком и react-hook-form не работал. И на глаза практически не попадался подход с вынесением общих пропсов в функции. А вот клонирование часто встречалось.

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

например как в вашем случае лучше решить кейс с выводом сообщения о текущем статусе валидации для поля формы

Для этого уже действительно лучше завести отдельный компонент, как у вас FormItem. Но это уже другая задача и нет необходимости объединять разный функционал в одном компоненте. FormItem так может разрастись и усложниться со временем.

У вас типичный и сомнительный вариант с использованием обертки и клонирования элемента ради вынесения общих props-ов. Не понимаю, почему всех тянет так делать. Задача ведь состоит в том, чтобы избавиться от дублирования пропсов. А значит надо для начала посмотреть, что можно сделать с пропсами, а не с элементом.

Вот более удобный вариант, который к сожалению мало кто видит:

<Input {...commonInputProps(name)} />

В функции commonInputProps уже нужная логика с сохранением/мемоизацией куда нужно по имени поля и возврат объекта с данными именно для этого поля.

Другими словами - разделение кода на логику и представление улучшает возможности повторного использования кода. Ничего нового, просто то же самое применимо и к компонентам. То есть можно разделить компонент на логику и функцию, которая просто получает props и не содержит ничего кроме JSX.
Нужна большая гибкость - разбиваем логику компонента на отдельные повторно используемые сущности. Еще большая - отделяем данные от логики.

Масштабно подобное не применяю, т.к. для этого потребуется самому писать и поддерживать обертку над react-ом, что затратно и выглядит не привычно для других разработчиков.
Применял бы, если бы писал библиотеку компонентов, используемую другими командами, т.к. react-way не очень хорош для этого.

Тоже была похожая ситуация. Только я ушел в геймдев на 3 года) Потом решил вернуться в веб и тоже не брали даже на джуна. С трудом нашел работу, самостоятельно более менее хорошо изучив современные на тот момент фронтенд технологии. Сейчас же я уже не по карману компаниям, в которых мне отказали.

Не понимаю причин подобных отказов. Ведь при этом эти же компании проводят стажировки, нанимают после них начинающих разработчиков. За 3 месяца стажировки, пока вырастят начинающего джуна, опытный из другого стека уже чуть ли не миддлом станет за счет предыдущего опыта.

1
23 ...

Information

Rating
Does not participate
Location
Омск, Омская обл., Россия
Registered
Activity