Комментарии 48
Неплохой альтернативой были бы объекты/компоненты в понимании Unity 3D — это достаточно удобная система.
Миксины к этому довольно близки, кстати, за исключением того что у них нет своих хуков жизненного цикла (их надо вызывать вручную).
Проблема уже давно решена в геймдеве. Жаль, что разработчики react и vue туда не заглядывали и свои велосипеды понаписали.
Spunreal
Про wrapper hell при использование HOC уже не раз писали сами разработчики реакта.
Всегда будут плохие кейсы использования паттернов. Любой паттерн нужно применять не ради применения, а где это нужно. Иначе любой паттерн может привести к паттерн-hell.
Заодно зову k12th
Спорный момент вытягивания gamedev паттернов в том, что там часто разменивают DX на производиьельность. С точки зрения дизайна api хуков в реакте очень удачное решение. Код группируется и выносится исходя из решаемых задач, а не вокруг методов жизненного цикла(и не приходится иметь десятки таких методов как юнити), при этом все вызовы явные и легко делятся данными. (причем обменном данными тоже явно управляет подключающий компонент, а не конвенции)
Из минусов сответственно производительность и запрет вызова хука в условных конструкциях. (что впрочем вызвало у нас проблемы лишь однажды за несколько месяцев использования и сразу решилось разделением компонента на 2)
Ну понятно, что если перенести этот прием из юнити прям один-в-один, то, вероятно, получится не очень — все-таки задачи разные и в геймдеве сложность куда выше. Но насчет DX, кажется, дело субъективное — мне лично в юнити приятнее работать, чем с реактом.
Вот как это выглядело:
объявление в самом компоненте.
class MyComponent1 extends ComponentWithBehaviors {
behaviors: [
new Behavior1(behavior1Props),
new Behavior2(behavior2Props),
]}
либо вариант с передачей в компонент через родителя.
<MyComponent2 components={[
new Behavior1(behavior1Props),
new Behavior2(behavior2Props),
]} />
Когда нужно выносить много логики, возможно такой подход был бы хорош. Но в простых случаях, когда достаточно 1-2 миксин/HOC, немного многовато кода придеться писать.
Интересно. А это был плагин для какого фреймворка?
Выложена одна из сохранившихся промежуточных версий — github.com/sergeysibara/react-behaviours
Миксины модифицируют базовый компонент. HOC же возращает новый, а оригинальный так и останется нетронутым. (с) Расширяй, но не изменяй.
Так же с помощью HOC можно изменить логику работы библиотечных компонентов не сломав оригинал.
На мой взгляд, ниже приведенная конструкция наследования, аналогично HOC, более наглядна и удобоварима:
//SecondComponent
import FirstComponent from './FirstComponent'
export default { extends: FirstComponent }
Ссылка на документацию: ru.vuejs.org/v2/api/#extends
Расписаный пример: vuejsdevelopers.com/2017/06/11/vue-js-extending-components
Если рассматривать все механизмы повторного использования кода во Vue, а не только mixin, то необходимо упомянуть плагины (как механизмы добавления функциональность на глобальном уровне) и provide / inject (как механизм добавления функциональности в цепочке наследования от родителя к потомкам).
Благодарю, ибо дискуссия позволяет глубже вникнуть в суть вопроса! И спасибо за статью, знания никогда не бывают лишними.
HOC — это лишь паттерн. У каждого паттерна есть своя зона применения. Если он не к месту, то он только ухудшит положение. Миксины и extends всё так же полезны, просто в некоторых моментах они проигрывают HOC, а в некоторых выигрывают.
Шаблон можно вынести в отдельный файл, написанный на pug или другом шаблонизаторе, поддерживающем наследование/импорт и затем унаследовать его
1) А если я не использую pug или другой шаблонизатор?
2) Появляется неявная зависимость в коде.
Решение выглядит как большой костыль в попытках расширить непредусмотренную функциональность extend.
Три раза прочитал, так и не понял зачем все это. Нужна кнопка с логированием, сделайте компонент назовите LoggerButton и используйте его, там где нужно логирование. Нужно куча всякого функционала, создайте кнопку MyButton, добавьте необходимые св-ва и в соответствии с ними меняйте логику поведения.
Или я не правильно понял поставленной задачи
Один компонент MyButton у него свойство settings: {
logger: boolean,
sound: boolean,
popup: boolean
}
И обрабатывайте как вам нужно
Чем это хуже кучи непонятных обёрток?
Ну и ниже уже amelekhin написал про принцип единой ответственности. Не должен компонент
Button
свистеть, пердеть и танцевать вприсядку.Нужная свистящая кнопка? Оборачивай хоком или делай компонент
WhistlingButton
.А если нужен другой компонент со звуком? Ну там панелька какая-нибудь. В этом случае можно переиспользовать HOC и не выдумывать новые компоненты со своими settings. Суть именно в этом. HOC представляет собой независимый слой логики.
"если нужен другой компонент со звуком?"
"не выдумывать новые компоненты"
Так нужен компонент или не нужен?
HOC — это костыль из React, зачем его тащить туда, где даже нет таких архитектурных косяков, ради которых его придумали?
github.com/yyx990803/vue-hooks
Вообще хуки прикольные, но они не заменят HOC на 100%. У них много общих задач, которые они решают, но есть и различия.
1) Если на компонент нужно несколько оберток, то, как написали в первом комменте, приходиться долго разбираться, что там происходит, разматывая эти обертки.
2) Нет разделения props.
По хорошему, каждая обертка должна получать только props, с которыми она работает. А компонент, только свои props. В HOC в принципе можно сделать так, но он не требует этого и в нем нет готового механизма для этого. Поэтому в больших проектах с активным использованием HOC получается жесть.
Имхо, Material UI для react и react-admin (от Marmelab) — хорошие примеры того, к чему HOC могут привести.
React devtools показывает с десяток HOC на каждом компоненте. Исходники тоже как-то страшновато выглядят.
В общем, я бы не рекомендовал использовать HOC.
Ден итак своими Redux и HOC реакт подпортил, давайте хоть в Vue не будем тянуть его наработки) Пусть другим путем идет.
Ну и в любом случае делают так, что на на вход получаем один набор props, а передается дальше измененный набор props. (Например, в HOC для валидации как минимум добавится prop «прошла ли валидация»).
Возьмём к примеру паттерн декоратор (на примере любого классического серверного языка, пусть даже PHP). Его концепция позволяет сделать то же самое — обернуть какой-нибудь объект в 10 обёрток, тем самым вызвать wrapped-hell. Но почему-то им до сих пор пользуются и пользуются успешно. Всё потому, что используют его там, где он принесёт много пользы, а не везде.
Нет идеального паттерна, есть хорошие паттерны для решения конкретных проблем.
В HOC в принципе можно сделать так, но он не требует этого и в нем нет готового механизма для этого.
HOC — это частный случай HOF (Higher Order Function). Вся концепция HOC — это получить один (на самом деле можно и несколько, но это особо не важно) компонент параметром и на основе полученных данных выплюнуть другой компонент. В целом вообще без разницы, что за код там будет внутри. Хотите как-то делить props? Пожалуйста, никто не запрещает. Хотите добавить какие-то сложные условия работы? Пожалуйста, никто не запрещает. Хотите с помощью HOC реализовать паттерн Container Component? Опять же, никто не запрещает.
Прокачиваем разработку на Vue с помощью паттернов: HOC