Я согласен, что на рамде можно знатно наговнокодить, сам такое видел не раз. Просто у людей которые только освоили рамду возникает соблазн писать на рамде все, часто это приводит к ужасному кода. JS нужно уметь правильно комбинировать подходы, это не сложно. Когда есть чистая логика - ООП явно ни к чему. Можете например посмотреть код @MaZaAa ниже
Допустим у вас есть логика которая дублируется в методах разных классов, и вы не можете использовать паттерн декоратор так как логика в средине. У вас есть выбор:
1) создать процедуру и уйти в процедурное программирование (самое плохое что можно сделать)
2) создать отдельный класс + агрегация + делегирование
3) продолжать дублировать код.
Какой вариант вам нравится больше всего?
Это как раз та проблема, из-за которой реакт перешёл на хуки. Иногда сложно быть DRY на ООП.
Локализация мутаций и сайд эффектов одна из главных проблем которые в свою очередь решила ООП. Но если уменьшить количество мутаций с помощью и описывать логику с помощью чистых функций. Использовать инкапсуляцию для сайд эффектов и логики но при этом не смешивать их в один метод, то можно решить часть проблем, которые ООП создал.
Но конечно у всего есть своя цена, писать иммутабельные обновление в мутабельном языке бывает не очень удобно. Есть ряд опять же не идеальных решений которые позволяют это упростить, например immer
Главный недостаток в том, что к классам очень сложно применить декомпозицию и композицию когда они разростаются. Вот напишите какой-то здоровенный класс, а потом попробуйте попробуйте его разбить на несколько классов и объединить их между собой да ещё так чтобы вся логика была в дочерних классах. Скорее всего вы сделаете это с помощью агрегации и делегирования (вы ведь не будете делать это с помощью наследия правда? :)) и будете правы. Но с чистыми функциями проводить декомпозицию и композицию намного проще и безопаснее.
Есть ещё много нюансов но у меня нет времени все описывать, рекомендую книгу выше, там все проблемы классов описаны подробно
Изменения состояния по своей сути это просто связь (state, event) -> newState чистая логика, соответственно чистая функция идеально подходит, чтобы это изменение выразить.
Проведите пример иммутабельного изменения которые выносит мозг, а я покажу как написать его так, чтобы не выносило мозг.
Основное преимущество чистых функций для стейт менеджмента в том, что вам не нужно все держать в одной функции, любую часть выражения которая будет хорошей абстракций можно вынести в спомогательную функцию, а функции можно потом объединить так, что получится почти текст на английском, о том, что делает результат :)
Есть ведь не только Redux, ещё куча библиотек для работы с иммутабельных состоянием. Все зависит от сложности вашего приложения и предпочтений. XState для сложного, Recoil для простого. А иногда и вообще можно обойтись каким-то react-query и useState с useReducer.
Но мне нравится @reduxjs-toolkit за его универсальность
JS не плохо подходит для FP и И это не только мое мнение. Для иммутабельности есть так же много всегда, линзы в рамде или иммер например, lodash/fp есть удобные утилиты. Для простых случаев, где состояние не сильно вложенно есть spreed.
Работа с массивами в иммутальном формате даже проще и читабельный
Те кто пишут код после меня не страдают, а разделяют такие же взгляды и понимают подход, нас таких много :) И то, что вы не понимаете преимущества чистых функций и проблемы ООП это только ваша беда. Вы можете отрицать и делать вид, что проблем ООП не существует сколько угодно, но это не решение проблемы.
Сложно не заметить общую тенденцию в React по движению в сторону ФП. Но вы обвиняете всех вокруг в некомпетентности. У вас не закрадывается мысль что это вы что-то упускете, а не все остальные (включая разработчиков Facebook)?
У чистых функций слишком много преимуществ чтобы я мог перечислить их здесь, к тому же вы все-равно не поймёте. Чтобы объяснить это вам, со всеми вашими стереотипами нужна целая книга, и такая книга есть. Это composition software by Eric Elliott
Мне с телефона не удобно смотреть сендбокс, но мутабельность и классы, ООП там где оно ни к чему несут за собой недостатки с которыми мне бы не хотелось мириться.
Да я фанат иммутабельности, но в первую очередь чистых функций. Нет абстракций лучше чем чистые функции. И я не хочу использовать классы и ООП там, где прекрасно справляются чистые функции.
Я не говорю что MobX это плохо всегда. В конце концов он позволяет справляться с задачами простой и средней сложности достаточно эффективно. Однако это не панацея. Идеального подхода нет
Насколько я пробовал, MobX не позволяет описывать логику изменений с помощью чистых функций и иммутабельных данных. Может что-то не понимаю, но у меня получалось, что его система реактивности ломалась, так как он следит именно за мутациями
Держать вложенность абстракций максимально низкой идея правильная, я считаю это важным для соблюдения KISS.
Если зацикливаться на использовании не более N уровней можно прийти к нарушению DRY.
Проблему с глубокой вложенностью абстракций можно получить абсолютно с чем угодно. Это могут быть хуки, классы, методы классов, объекты, даже чистые функции. И никакой линтер тут не поможет.
О разбросывании этого всего добра по разным файлам вообще молчу
Я согласен, что когда есть селекторов на 100 строк, то держать их в файле со slice может быть неудобно и тогда, возможно твой подход имеет смысл. Но если у меня 11 довольно простых селекторов на 25 строк, я знаю что супер сильно их количество не увеличится, то отдельный файл как-бы и не нужен. Не знаю как тебя, а меня бесит когда куча файлов по 10 строк. Я считаю что создавать папки и выносить в отедельные файлы имеет смысл когда кода действительно становится много (или если это очевидно сразу). Это ведь можно сделать без сайд эффектов для остальной кодовой базы.
доке очень много плохого кода
Я прямо плохого кода там не замечал, видел упрощенный, но об этом всегда пишут. Ты всегда можешь законтрибьютить в доку
Эта рекомендация вообще для новичков
Ну как бы нет, там такого не написано, style-guide он для всех
таким именованием и подходами все превращается в мусорку
О чем это ты? С чем-то еще не согласен из redux style-guide или просто не согласен со мной?
А насчет автоимпорта, у тебя в примере ты делаешь вот так:
// counter/model/index.js
export * as counterSelectors from './selectors';
Когда напишем counterSelectors в новом файле редактор ничего не подтянит так как экспорта с таким именем нет, чтобы подтягивал нужно объеденять все в объект counterSelectors и именуемый экспорт делать, что практически сводит на 0 ведь подход. Имя домена будет упомянуто 1 раз, это преимущество, но за это прийдется платить добавлением каждого селекта в объект counterSelectors для экспорта.
Если у тебя это как-то работает и так поделись секретом :)
Понимаю твою мотивацию, не говорю что делать так как ты предлогаешь это не правильно, я так же делал одно время, лень было упоминать имя slice в каждом селекторе. Но сейчас я предпочитаю объявлять все селекторы в файле для slice. И то что оно в одном файле никак не мешает наоборот удобно так как все что касается этого слайса в одном месте.
Селекторы - чистые функции, не понимаю какие проблемы с дебагом тут могут быть. Чистые функции самое пригодное для дебага что только может быть.
Проблемы с вложенностью можно получить используя что угодно, даже тот же MobX если навкладывать вычисляемые свойства один в другой несколько раз. Просто нужно контролировать вложеннсть абстракций. В случае с чистыми функциями это супер просто так как часто можно заменить вкладывание на функциональную композицию. В случае с классами, методами и остальными более сложными абстракциями дела обстоят куда сложнее.
Если получится большая вложенность вероятность велика что декомпозиция была выполнена не оптимально
Не нужно писать 5 функций, можно писать по 1 селектру для каждого нужного снаружи поля и дублировать пути в разных селекторах. Так будет проще читать так как сразу видно весь путь и нет кучи вложенных функций
Думаю автор хотел показать саму суть того, что селекторы можно вкладывать и переиспользовать код, но злоупотреблять этим не стоит
Хорошая статья, вы много чего очень правильно рассказали несчитая нескольких мелких нюансов.
Re-reselect пытается обобщить решение теоретической проблемы для которой скорее всего найдется более очевидное и простое решение в конкретном случае.
В композиции селекторов нужно найти баланс, нет смысла за-DRY-ивать и усложнять код который состоит из одной строки. Иногда лучше продублировать путь ещё раз чем вкладывать функции. Это упростит чтение.
counterSelectors.count ничем не короче писать в итоге чем selectCounterCount. Вот только второй вариант можно удобно автоимпортить и он рекомендуется стайлгайдом.
Вот только useCallback сам по себе ничего не оптимизирует. Чтобы что-то соптимизировать его нужно использовать вместе с memo (или чем-то другим, что требует стабильности ссылок). Поскольку здесь memo не используется, то и в useCallback нет никакого смысла.
К тому же мемоизация всего подряд без разбора скорее приведет к усложнению поддержки чем к лучшей производительности. Используйте инструменты для оптимизации когда что-то работает недостаточно быстро для пользователей, а не для профилактики
Я согласен, что на рамде можно знатно наговнокодить, сам такое видел не раз. Просто у людей которые только освоили рамду возникает соблазн писать на рамде все, часто это приводит к ужасному кода. JS нужно уметь правильно комбинировать подходы, это не сложно. Когда есть чистая логика - ООП явно ни к чему. Можете например посмотреть код @MaZaAa ниже
Допустим у вас есть логика которая дублируется в методах разных классов, и вы не можете использовать паттерн декоратор так как логика в средине. У вас есть выбор:
1) создать процедуру и уйти в процедурное программирование (самое плохое что можно сделать)
2) создать отдельный класс + агрегация + делегирование
3) продолжать дублировать код.
Какой вариант вам нравится больше всего?
Это как раз та проблема, из-за которой реакт перешёл на хуки. Иногда сложно быть DRY на ООП.
Локализация мутаций и сайд эффектов одна из главных проблем которые в свою очередь решила ООП. Но если уменьшить количество мутаций с помощью и описывать логику с помощью чистых функций. Использовать инкапсуляцию для сайд эффектов и логики но при этом не смешивать их в один метод, то можно решить часть проблем, которые ООП создал.
Но конечно у всего есть своя цена, писать иммутабельные обновление в мутабельном языке бывает не очень удобно. Есть ряд опять же не идеальных решений которые позволяют это упростить, например immer
Главный недостаток в том, что к классам очень сложно применить декомпозицию и композицию когда они разростаются. Вот напишите какой-то здоровенный класс, а потом попробуйте попробуйте его разбить на несколько классов и объединить их между собой да ещё так чтобы вся логика была в дочерних классах. Скорее всего вы сделаете это с помощью агрегации и делегирования (вы ведь не будете делать это с помощью наследия правда? :)) и будете правы. Но с чистыми функциями проводить декомпозицию и композицию намного проще и безопаснее.
Есть ещё много нюансов но у меня нет времени все описывать, рекомендую книгу выше, там все проблемы классов описаны подробно
Изменения состояния по своей сути это просто связь (state, event) -> newState чистая логика, соответственно чистая функция идеально подходит, чтобы это изменение выразить.
Проведите пример иммутабельного изменения которые выносит мозг, а я покажу как написать его так, чтобы не выносило мозг.
Основное преимущество чистых функций для стейт менеджмента в том, что вам не нужно все держать в одной функции, любую часть выражения которая будет хорошей абстракций можно вынести в спомогательную функцию, а функции можно потом объединить так, что получится почти текст на английском, о том, что делает результат :)
Есть ведь не только Redux, ещё куча библиотек для работы с иммутабельных состоянием. Все зависит от сложности вашего приложения и предпочтений. XState для сложного, Recoil для простого. А иногда и вообще можно обойтись каким-то react-query и useState с useReducer.
Но мне нравится @reduxjs-toolkit за его универсальность
JS не плохо подходит для FP и И это не только мое мнение. Для иммутабельности есть так же много всегда, линзы в рамде или иммер например, lodash/fp есть удобные утилиты. Для простых случаев, где состояние не сильно вложенно есть spreed.
Работа с массивами в иммутальном формате даже проще и читабельный
Те кто пишут код после меня не страдают, а разделяют такие же взгляды и понимают подход, нас таких много :) И то, что вы не понимаете преимущества чистых функций и проблемы ООП это только ваша беда. Вы можете отрицать и делать вид, что проблем ООП не существует сколько угодно, но это не решение проблемы.
Сложно не заметить общую тенденцию в React по движению в сторону ФП. Но вы обвиняете всех вокруг в некомпетентности. У вас не закрадывается мысль что это вы что-то упускете, а не все остальные (включая разработчиков Facebook)?
У чистых функций слишком много преимуществ чтобы я мог перечислить их здесь, к тому же вы все-равно не поймёте. Чтобы объяснить это вам, со всеми вашими стереотипами нужна целая книга, и такая книга есть. Это composition software by Eric Elliott
https://medium.com/javascript-scene/composing-software-the-book-f31c77fc3ddc
Мне с телефона не удобно смотреть сендбокс, но мутабельность и классы, ООП там где оно ни к чему несут за собой недостатки с которыми мне бы не хотелось мириться.
Да я фанат иммутабельности, но в первую очередь чистых функций. Нет абстракций лучше чем чистые функции. И я не хочу использовать классы и ООП там, где прекрасно справляются чистые функции.
Я не говорю что MobX это плохо всегда. В конце концов он позволяет справляться с задачами простой и средней сложности достаточно эффективно. Однако это не панацея. Идеального подхода нет
Вы большой фанат MobX я смотрю.
Насколько я пробовал, MobX не позволяет описывать логику изменений с помощью чистых функций и иммутабельных данных. Может что-то не понимаю, но у меня получалось, что его система реактивности ломалась, так как он следит именно за мутациями
Держать вложенность абстракций максимально низкой идея правильная, я считаю это важным для соблюдения KISS.
Если зацикливаться на использовании не более N уровней можно прийти к нарушению DRY.
Проблему с глубокой вложенностью абстракций можно получить абсолютно с чем угодно. Это могут быть хуки, классы, методы классов, объекты, даже чистые функции. И никакой линтер тут не поможет.
О разбросывании этого всего добра по разным файлам вообще молчу
Я согласен, что когда есть селекторов на 100 строк, то держать их в файле со slice может быть неудобно и тогда, возможно твой подход имеет смысл. Но если у меня 11 довольно простых селекторов на 25 строк, я знаю что супер сильно их количество не увеличится, то отдельный файл как-бы и не нужен. Не знаю как тебя, а меня бесит когда куча файлов по 10 строк. Я считаю что создавать папки и выносить в отедельные файлы имеет смысл когда кода действительно становится много (или если это очевидно сразу). Это ведь можно сделать без сайд эффектов для остальной кодовой базы.
Я прямо плохого кода там не замечал, видел упрощенный, но об этом всегда пишут. Ты всегда можешь законтрибьютить в доку
Ну как бы нет, там такого не написано, style-guide он для всех
О чем это ты? С чем-то еще не согласен из redux style-guide или просто не согласен со мной?
Да ты прав, это все моя невнимательность, я недосмотрел что это експорт
А насчет автоимпорта, у тебя в примере ты делаешь вот так:
Когда напишем counterSelectors в новом файле редактор ничего не подтянит так как экспорта с таким именем нет, чтобы подтягивал нужно объеденять все в объект
counterSelectors
и именуемый экспорт делать, что практически сводит на 0 ведь подход. Имя домена будет упомянуто 1 раз, это преимущество, но за это прийдется платить добавлением каждого селекта в объектcounterSelectors
для экспорта.Если у тебя это как-то работает и так поделись секретом :)
Я об этом
https://redux.js.org/style-guide/style-guide#name-selector-functions-as-selectthing
Понимаю твою мотивацию, не говорю что делать так как ты предлогаешь это не правильно, я так же делал одно время, лень было упоминать имя
slice
в каждом селекторе. Но сейчас я предпочитаю объявлять все селекторы в файле дляslice
. И то что оно в одном файле никак не мешает наоборот удобно так как все что касается этого слайса в одном месте.slice
ведь и так знает что он 'plp', так что никакой проблемы с внешним и внутренним конкретно в именовании нет.Вообще это достаточно незначительный вопрос именования, я просто предпочитаю делать то, что говорят в доке
Селекторы - чистые функции, не понимаю какие проблемы с дебагом тут могут быть. Чистые функции самое пригодное для дебага что только может быть.
Проблемы с вложенностью можно получить используя что угодно, даже тот же MobX если навкладывать вычисляемые свойства один в другой несколько раз. Просто нужно контролировать вложеннсть абстракций. В случае с чистыми функциями это супер просто так как часто можно заменить вкладывание на функциональную композицию. В случае с классами, методами и остальными более сложными абстракциями дела обстоят куда сложнее.
Если получится большая вложенность вероятность велика что декомпозиция была выполнена не оптимально
Не нужно писать 5 функций, можно писать по 1 селектру для каждого нужного снаружи поля и дублировать пути в разных селекторах. Так будет проще читать так как сразу видно весь путь и нет кучи вложенных функций
Думаю автор хотел показать саму суть того, что селекторы можно вкладывать и переиспользовать код, но злоупотреблять этим не стоит
Хорошая статья, вы много чего очень правильно рассказали несчитая нескольких мелких нюансов.
Re-reselect пытается обобщить решение теоретической проблемы для которой скорее всего найдется более очевидное и простое решение в конкретном случае.
В композиции селекторов нужно найти баланс, нет смысла за-DRY-ивать и усложнять код который состоит из одной строки. Иногда лучше продублировать путь ещё раз чем вкладывать функции. Это упростит чтение.
counterSelectors.count ничем не короче писать в итоге чем selectCounterCount. Вот только второй вариант можно удобно автоимпортить и он рекомендуется стайлгайдом.
Надеюсь этот комментарий полезен :)
Вот только useCallback сам по себе ничего не оптимизирует. Чтобы что-то соптимизировать его нужно использовать вместе с memo (или чем-то другим, что требует стабильности ссылок). Поскольку здесь memo не используется, то и в useCallback нет никакого смысла.
К тому же мемоизация всего подряд без разбора скорее приведет к усложнению поддержки чем к лучшей производительности. Используйте инструменты для оптимизации когда что-то работает недостаточно быстро для пользователей, а не для профилактики
Мне не так давно пришла в голову очень похожая идея, вот только я сделал это немного по другому, на основе RTK (redux toolkit).
создал issue на githab в RTK:https://github.com/reduxjs/redux-toolkit/issues/1065
А как насчёт варианта из Ramda?
https://ramdajs.com/docs/#sortWith