Comments 49
github.com/vuejs/rfcs/blob/dynamic-lifecycle/active-rfcs/0000-dynamic-lifecycle-injection.md
Почему нельзя совмещать хуки и хоки? Просто нужно без фанатизма. Это не противостояние за власть, а разные инструменты для разных задач. Я как бы понимаю, что и микроскопом можно гвозди забивать, но лучше забивать гвозди молотком, а с микроскопом делать другое.
Я как бы понимаю, что и микроскопом можно гвозди забивать, но лучше забивать гвозди молотком, а с микроскопом делать другое.
Забавно, что вся история с хуками начинается как раз с забивания гвоздей чем угодно кроме молотка — если при проектировании интерфейса не смешивать модель с представлением, то хуки особо и не сдались (потому что представляют собой всего лишь более сильный инструмент для отделения модели и её логики от представленческих вещей типа цикла жизни компонент). Но поскольку модель и логика у фронтэндщиков частенько очень простая и вырожденная — оные фронтэндщики очень любят вписать её сразу в компоненты, а уже потом начать думать, что же с этим делать. И вот тут-то и хуки становятся полезными.
Захотел разделения бизнес логики и представления — используешь паттерн Container Component. Захотел изменить что-то в одном месте не меняя поведение в других — HOC. Захотел всё смешать в кучу — написал через хуки.
Только везде учат, что паттерны должны быть к месту, а не ради использования паттернов, но в React какой-то хайп идёт по каждому новому паттерну (или функционалу, который именуют очередным убийцей). То HOC самый лучший паттерн, который заменяет почти всё, потом появился убийца HOC'а — Render Props, а теперь уже и хуки, которые опять же сравнивают на полном серьёзе с HOC и Render Props как их замену.
Такое ощущение, что все просто поклоняются тому, о чём пишет Дэн Абрамов. Он написал, что HOC лучший — все это подхватили. Он написал, что HOC ужасен и Render Props теперь лучший — всё форсят Render Props.
Видимо молодость фронтенда даёт о себе знать. Когда-нибудь большая часть паттернов устаканится и их правильное применение осядет в головах, как это происходит в бэкенде. А сейчас идёт поиск серебряной пули.
Захотел разделения бизнес логики и представления — используешь паттерн Container Component.
Это шаг один. На шаге два обычно приходит понимание, что модель и логика — она вообще не обязана относиться к компонентам и уж тем более реакту: это просто код, ему пофиг на эти ваши фреймворки и либы по большей части, он и в реакте, и в ангуляре, и в чем угодно вообще будет выглядеть примерно одинаково с поправками на интеграционные рюшечки с представлением.
Во фронтэнде слишком часто встречается проталкивание инструмента везде, где только можно, даже если он не нужен. Есть реакт и его компоненты? А давайте у нас всё-всё будет «компонентом», даже если объективно это вообще ни разу не компонент? А давайте!
Это вот вообще классическое «когда у вас в руках молоток, все проблемы выглядят незабитыми гвоздями».
В клиентском коде логика представления очень часто неотделима от компонента, и кроме как в нем больше нигде не нужна, если мы не говорим о какой-то сложной бизнес логике. Пример — какой-нибудь разворачивающийся блок, выпадающее меню, кнопка со спиннером. Если такую логику начинать выносить — оно вроде как правильно с точки зрения паттернов, но выглядит крайне монструозно. Если слепо всем паттернам следовать — получается Java которую невозможно без крови в глазах читать от словоблудия. JS тем и хорош и элегантен, что можно write less do more. За это надо платить. И это риск. Главное головой думать и баланс соблюдать между "как надо" и "как проще".
Хуки кстати позволяют логику вынести легким движением руки на любой стадии, что хорошо.
В клиентском коде логика представления очень часто неотделима от компонента, и кроме как в нем больше нигде не нужна, если мы не говорим о какой-то сложной бизнес логике.
Смысл не в том, что она нужна где-то еще, смысл в том, чтобы не было мешанины, которую потом трудно поддерживать. В фреймворках с темплейтами отсутствие мешанины форсится самим фреймворком, то есть намешать нельзя by design. В случае реакта намешать можно, по-этому изобретаются способы, как этого не делать — smart/dumb компоненты, рендер пропсы, хуки вот и так далее.
В Angular в темплейте будь здоров можно логики намешать, впрочем, как и везде.
HOC, RP и Hooks не просто про разделение, а про возможность выделить некий код как общий и переиспользовать его. Это библиотечные вещи в первую очередь, как подключить что-то внешнее к компоненту. А как ворочать локальным стейтом — дело десятое, если этого нигде снаружи не видно.
В Angular в темплейте будь здоров можно логики намешать, впрочем, как и везде.
Нельзя, потому что набор конструкций, которые можно использовать в темплейте, существенно ограничен.
HOC, RP и Hooks не просто про разделение, а про возможность выделить некий код как общий и переиспользовать его. Это библиотечные вещи в первую очередь, как подключить что-то внешнее к компоненту.
Нет, это в первую очередь про то, как отделить то самое "внешнее" от самого компонента.
набор конструкций, которые можно использовать в темплейте, существенно ограничен
Знаете сколько диких конструкций с пайпом и наворотами я видел в ngFor https://angular.io/api/common/NgForOf#local-variables. Я не спорю, что с темплейтом прострелить себе ногу сложнее, но зато и выразительность хуже, в JSX можно очень красивые вещи делать, если с умом подходить.
не просто про разделение, а про возможность выделить некий код как общий и переиспользовать его
в первую очередь про то, как отделить то самое "внешнее" от самого компонента
Вы по-моему то же самое написали, но другими словами. У меня акцент на то, что само по себе выделение должно быть оправдано и должно служить некой цели. Если компонент вещь в себе и логика его никому более не нужна — нет смысла что-то из него выделять в HOC/RP/Hook. Выделять имеет смысл то, что можно переиспользовать.
Знаете сколько диких конструкций с пайпом и наворотами я видел в ngFor https://angular.io/api/common/NgForOf#local-variables.
Так это и есть элементарные конструкции, не содержащие какой-либо логики, с которыми нет никаких проблем.
но зато и выразительность хуже, в JSX можно очень красивые вещи делать, если с умом подходить.
Эти "красивые вещи" как раз то, чего не должно быть в логике рендера, так что ничего красивого в них на самом деле нет.
Рендер должен иметь только одна ответственность — с-но, рендерить. В случае templatye-based подхода это условие выполнено и в итоге вам достаточно краткого взгляда на темплейт, чтобы понять, что вы получите в результате рендеринга компонента. В случае реакта — у вас plain js, который может делать все что угодно, с-но, эту логику еще распарсить надо (что часто нетривиально), там же обычно происходит и обработка данных. Это не говоря уже о частом использовании антипаттернов вроде HOC/render props, когда в верстке зачем-то оказываются вещи вроде "эта компонента забирает данные оттуда", что вообще в верстке недопустимо и за что следует просто отрывать руки и вон из отрасли.
Если компонент вещь в себе и логика его никому более не нужна — нет смысла что-то из него выделять в HOC/RP/Hook.
Правильно, давайте у нас за загрузку данных по сети будет отвечать слой рендеринга. Действительно, рендеру же делать нечего больше.
Вы же понимаете что вещи вроде best preactices а-ля "не мешайте логику и вид" написаны, что называется, кровью? Все это уже было, давным-давно, и все это было вытеснено более современными и надежными подходами.
А ваша логика про "удобство" эта логика из разряда что "работать без шлема удобнее" или "солдату удобнее на посту спать". Ну с этим трудно спорить. Действительно — удобнее. Тем более что через два года с вероятностью 90% типичный фронтендщик сменит место работы и поддерживать то, что он наговнякал, ему не придется. А новый месье скажет "давайте выкинем это говно и напишем новое".
Я несколько более прагматично смотрю. Уровень темплейтов — это самый незначительный уровень в нормальном большом приложении. Все реально важные модули, где нужна архитектура — выше. Если, например, мы говорим о приложениях, которые ворочают данными, особенно в реальном времени, то там ядро данных будет скорее всего вообще ничего не знать о том Vue у вас или допустим Angular или React, потому что там MobX/Redux/RX/plain-vanilla/что-угодно. И по большому счету при правильном подходе должно быть все равно что творится в шаблонах, т.к. верстку переделывают и меняют часто, в том числе полностью с нуля, потому что последние A/B тесты показали, что как было — не годится. И за умную логику в шаблонах/компонентах вместо ядра данных — по голове дадут так и так.
Более того, работать приходится не только с мастерами экстра-класса, поэтому да, скорость разработки в формате "без шлема", а также дешевизна кода, который с большой долей вероятности будет выкинут — это крайне важные вещи.
С точки зрения "по учебнику" я с Вами согласен, но в жизни важны совсем другие вещи. Когда в системе 6000 модулей все должно быть чисто и аккуратно на многих уровнях, и если все правильно сделано на остальных уровнях, шаблон там или не шаблон в глубине уже не имеет значения.
И по большому счету при правильном подходе должно быть все равно что творится в шаблонах, т.к. верстку переделывают и меняют часто, в том числе полностью с нуля
Вот именно по-этому в шаблонах и не должно быть логики, все верно. Иначе переделка верстки легким движением руки превращается в полноценный рефакторинг со всеми вытекающими.
Некоторые jquery плагины применяют свою логику в зависимости от добавленных атрибутов. Т.е. там общая логика тоже вынесена из представления.
Разве это выглядит монструозно?
Но вместо вынесения логики представления в атрибуты у нас есть всякие миксины, hoc, теперь еще и hooks.
В общем, хочу сказать следующее. Прихожу к выводу, что нынешние фронтенд фреймворки допустили общую архитектурную ошибку и предлагаются разные решения вокруг этой ошибки.
Я о разделении ответственности. Компонент занимается и логикой и отображением, что нарушает принцип единственной ответственности.
Сейчас в классе/функции пишется template/метод render. Там же пишется обработка логики.
Либо часть логики выносится в миксины, HOC, куда-то еще.
Но суть от этого не меняется. Вся эта дополнительная логика привязывается к одной сущности — компоненту, который занимается еще и отображением.
Получается, что связь между отображением и логикой — один к одному.
В итоге методы, данные, либо props так или иначе смешаны в одном компоненте.
Так же быть не должно!
Можно сделать по другому:
- Компонент занимается только отображением.
- В нем нет методов жизненного цикла.
- К компоненту можно подключать сколько угодно сущностей с логикой (не знаю, как лучше назвать, поэтому пусть будет сущность с логикой), где у каждой сущности есть свое состояние и прочая логика, включая методы жизненного цикла.
И проблема с пересечением данных была бы решена и для компонентов не были бы нужны всякие миксины, HOC, Container Component и прочее. Плюс легко в рантайме можно было бы добавлять/удалять логику.
При этом аналог HOC с логикой может быть полезен, но не для обертки над компонентом, а для обертки над сущностью с логикой.
JButton button = new JButton("Button");
ActionListener actionListener = new MyActionListener();
button.addActionListener(actionListener);
и react'овским<button onClick={handleClick} />
или angular'овским<button (click)="onClick()">Button</button>
?
JTable table = new JTable();
Model model = new Model();
table.setModel(model);
Controller controller = new Controller(model);
button.addActionListener(controller);
а в реакте<Table model={model} />
Вопрос остается тем же — в чем фундаментальное отличие от реакта и, особенно, от angular?
Возвращаясь к вашим примерам, в реакте очень редко можно увидеть таблицу, как написано у вас, и очень часто — вот так:
<Table data={data} />
Думаю, вам понятно, чем это отличается от свинга, в котором так сделать попросту нельзя.
В чем фундаментальное отличие между
В количестве словоблудия )
Но привести прям пример UI мне сложно, искать надо.
Скажу, что UI там строится по тому же принципу, что и все остальное в проекте. Про это можно здесь почитать:
docs.unity3d.com/ru/current/Manual/GameObjects.html
docs.unity3d.com/ru/current/Manual/UsingComponents.html
Чтобы понятней было, можно еще тут про ECS почитать:
habr.com/ru/post/358108 (пункт «Что такое ECS»)
Покажите мне простой пример паттерна декоратор или адаптер на хуках. Покажите мне аналог scoped-slots на хуках. Окажется ли это проще, чем с HOC или Render Props?
Ну и главный вопрос. Какой из этих молотков хуки:
const Template = ({ col1, col2 }) => (
<div>
{col1}
{col2}
</div>
);
const TemplateWithData = ({ data }) => (
<Template col1={<div>{data.first}</div>} col2={<div>{data.second}</div>} />
);
В вашей говорится о конкретном случае использования. Для этого я бы тоже не стал использовать HOC или Render Props. Но в данном кейсе мне больше симпатизирует паттерн Container Component. Всё-таки если захотим использовать тот же компонент, но с другой логикой получения данных, то в вашем случае придётся создавать ещё один компонент, в который прокинем новый хук. В случае контейнера мы просто создаём новый контейнер, а сам компонент не трогаем. А вообще хуки — это конструкция фреймворка. Их можно совмещать со всеми паттернами, будь то HOC, Render Props, Container Component или любой другой из известных и не очень, которые мы используем для разделения зон ответственности и связанности кода.
Но касательно вашего примера — вы написали обычные слоты. Для слотов с ограниченной областью видимости как раз надо передать функцию, т.к. параметры в неё должен передать дочерний компонент. А это уже Render Props.
Это может понадобиться при создании универсальных компонентов, например, автокомплита, когда элемент списка в разных местах может иметь разный шаблон. Т.е. мы говорим, что родитель может передать кусок шаблона в дочерний компонент, но в нём можно использовать только определённый набор параметров (к примеру {id, name, title}).
const Component1 = ({ getData }) => <div>{getData(1)}</div>;
const Component2 = ({ getData }) => <div>{getData(2)}</div>;
const WrapperRenderProp = ({ children }) => {
const DATA = [1, 2, 3, 4];
const getData = i => DATA[i];
return <div>{children(getData)}</div>;
};
const ResultWithRenderProp = () => (
<WrapperRenderProp>
{getData => (
<div>
<Component1 getData={getData} />
<Component2 getData={getData} />
</div>
)}
</WrapperRenderProp>
);
но это можно и так написать без renderProp
const Component1 = ({ getData }) => <div>{getData(1)}</div>;
const Component2 = ({ getData }) => <div>{getData(2)}</div>;
const WrapperWithProps = ({ Template1, Template2 }) => {
const DATA = [1, 2, 3, 4];
const getData = i => DATA[i];
return (
<div>
<Template1 getData={getData} />
<Template2 getData={getData} />
</div>
);
};
const ResultWithProps = () => (
<Wrapper Template1={Component1} Template2={Component2} />
);
Какой подход выбрать — нужно смотреть на детали. Если, например, есть лэйаут (WrapperWithProps), который в зависимости от темы рендерит разный Template1/Template2 и передает одни и те же пропсы я бы выбрала подход WrapperWithProps.
А если надо общий JSX но при этом супер нетривиальная логика с Template внутри я бы выбрала WrapperRenderProp. Но в любом случае это имеет смысл, когда есть общая верстка-раппер (общий JSX).
Посыл в статье с хуками про то, что удобно переиспользовать логику. Если нужно переиспользовать верстку, то композиция компонентов всегда была сильной стороной React — можно по-разному.
А кто-нибудь может пояснить, какую все-таки конкретно задачу решают хуки?
Ну то есть, если нужен компонент со стейтом — мы просто делаем компонент со стейтом, и хуки не нужны (пример с useRequest на обычном стейте без хуков реализуется точно так же, допустим). Если нужен компонент без стейта — мы делаем компонент без стейта, и хуки тоже не нужны.
В каком случае они нужны? Если хотя бы теоретически представить.
Хуки не делают ничего такого, чего нельзя сделать с классами. Это просто другой путь, который команда react-а взяла как за основной. В некотором роде классы теперь legacy.
Т.е. нужны они везде где у вас stateful компоненты и вы пишете код, в поддержке и развитии которого вы заинтересованы в будущем.
Команда react-a героически победила "проблему с this", заменив её на не менее сложную проблему с оркестрированием замыканий (не очень опытным разработчикам это страшно "выносит мозг", ибо код быстро становится неочевидным). Да и опытным ребусы гадать не всегда хочется. Самое простое это setState + async-useCallback. Всякие фокусы с useRef не для domElement-ов.
Я с декабря пишу используя хуки, очень нравится. Но не могу не отметить что это уже какой-то другой react. На мой взгляд писать приложение правильно с хуками сложнее, чем с классами. Требует большей квалификации. Как-то это дисгармонирует с их политикой партии. Хотели упростить, ведь "классы это сложнааа"...
уки не делают ничего такого, чего нельзя сделать с классами. Это просто другой путь, который команда react-а взяла как за основной. В некотором роде классы теперь legacy.
Как-то стало еще менее понятно. Зачем вообще этот другой путь тогда нужен, если он не поддерживается нативно языком, требует делать закат солнца вручную и при этом ничего нового не дает?
Команда react-a героически победила "проблему с this"
Это какая именно проблема имеется в виду?
Т.е. нужны они везде где у вас stateful компоненты и вы пишете код, в поддержке и развитии которого вы заинтересованы в будущем.
То есть, фактически, разработчики реакта взяли в заложники свое комьюнити? "Используйте наш кривой костыль вместо полноценного решения, иначе поддерживать не будем"?
Фигасе.
То есть, фактически, разработчики реакта взяли в заложники свое комьюнити? «Используйте наш кривой костыль вместо полноценного решения, иначе поддерживать не будем»?
Про поддержку классов пока что везде очень осторожные высказывания, что их поддержка никуда не денется и никак не поменяется. Не думаю, что им хочется повторять историю angularJS в исполнении реакта. Никто не побежит переписывать свой код с объектной парадигмы на функциональную просто потому, что разработчикам реакта так нравится больше.
Никто не побежит переписывать свой код с объектной парадигмы на функциональную
Хуки же это наоборот объектная парадигма. С-но, к ним основная претензия как раз и состоит в том, что непонятно, зачем нужна наколеночная реализация ООП, когда она уже есть нативная. Другое дело, если бы хуки появились лет 5+ назад, когда классы были еще совсем не в моде. Тогда свои костыльные ООП-подсистемы были еще актуальны.
Всё больше людей во фронте отдают предпочтение композиции вместо наследования.
Так с классами точно такая же композиция. Composition over inheritance — это же вообще ООП-шная придумка. Тут проблема в том, что вместо нативных, полноценных классов, которые поддерживаются на уровне языка, люди зачем-то делают свои, на коленке. Никаких разумных причин, очевидно, для этого быть не может, обычный NIH-синдром.
В результате вместо одной нативной реализации ООП, сейчас будет минимум три распространенных — нативная, реакт-хуки и вуе-хуки. И у каждой из них будет свое собственное темное прошлое, туманное будущее и мрачное настоящее.
Это какая именно проблема имеется в виду?
"this и ООП в JS это так сложно, что начинающие разработчики и не только путаются и пишут говно-код". Что-то в таком духе. ИМХО, мне было бы легче новичку объяснить что у него там с this сломалось, нежели объяснить почему в его async-callback-е не видно обновлённый state.
Фигасе
А то. Правда выпиливать классы пока никто вроде не собирается. Но каких-то заметных улучшений в этой области ждать не приходится, они хорошо дали понять, что right react way это хуки и хоки. react-team вообще тяготеет к таким вещам. Хотят везде pure-function, а где не получается, то хотя бы что-то издали похожее на pure-function :) Хотят композицию, иммутабельность, дробление и всё такое.
В целом мало-мальски сложное приложение сейчас состоит из такого разнообразного бульона подходов. И render-methods, и контекст, и классовые компоненты (error-boundaries), и слоты, и хоки, и хуки. Может ещё чего забыл. Приправьте к этому какой-нибудь redux (особенно если с proxy, селекторами, викмап-мемоизацией) или mobx и получиться того ещё монстра. Но мне нравится :)
"this и ООП в JS это так сложно, что начинающие разработчики и не только путаются и пишут говно-код"
Не понял ничего. Какая проблема с this-то? То, что он ведет себя не так как в других ООП-языках? Так это стрелочными ф-ми решается. Или что-то другое?
Хотят везде pure-function, а где не получается, то хотя бы что-то издали похожее на pure-function
Ну "казаться а не быть" — это вообще проблема реакт-экосистемы. Везде какие-то нелепые рассуждения про ФП и чистоту, хотя по факту реакторедаксы — чистой воды процедурщина и никакого ФП с чистотой там рядом даже не пробегало.
В целом мало-мальски сложное приложение сейчас состоит из такого разнообразного бульона подходов. И render-methods, и контекст, и классовые компоненты (error-boundaries), и слоты, и хоки, и хуки.
А потом говорят про простой порог вхождения, ага. Он, может, и был простой — в 2015.
И главное-то что это бесполезная сложность — т.к. в данном случае знать надо не как делается Х, Y и Z (что расширяет возможности), а 100 разных способов сделать Х.
Рекомендую прочесть и посмотреть это. Тут они попытались объяснить свою мотивацию. Мои пересказы этого текста вторичны :)
Пока писал ответ на меня снизошло озарение. Такое ощущение что экосистема React бодро так шагает к тому как писали PHP сайты 15 лет назад: помесь html, php, js, css & sql в одном файле. Ныне это JSX, JS || TS, cssInJS (и graphQL? :D).
Такое ощущение что экосистема React бодро так шагает к тому как писали PHP сайты 15 лет назад
https://en.wikipedia.org/wiki/XHP
С-но, да, "как в былые времена" — это основа реакта :)
Classes confuse both people and machines
Кек. Я не первый раз от команды реакта вижу это "confuse", оно нередко встречается в мотивации по всяким issues.
Но надо ли говорить, что у ребят какое-то свое странное мнение о том, что конфьюзит людей, а что — нет? :)
Conceptually, React components have always been closer to functions.
По-этому, чтобы не писать классы, давайте сделаем, чтобы эта функция от класса ничем не отличалась :)
Хуки — это способ использовать локальный стейт и вызывать сайд-эффекты (например запрос к API) в функциональных компонентах. До этого это было возможно только в компонентах-классах. В доках можно детали посмотре ь https://reactjs.org/docs/hooks-intro.html
Хуки — это способ использовать локальный стейт и вызывать сайд-эффекты (например запрос к API) в функциональных компонентах.
Так а зачем кому-то может захотеться использовать функциональный компонент, если в нем есть стейт? В этом и есть вопрос. Нафига это надо? Для чего? Какие проблемы в том, чтобы сделать компонент со стейтом классом?
Потому что это проще писать и меньше строчек кода. Если говорить про сайд-эффекты — useEffect — это ещё и безопаснее. В useEffect переданная функция будет вызван асинхронно в отличие от componentDidMount.
Потому что это проще писать и меньше строчек кода. Если говорить про сайд-эффекты — useEffect — это ещё и безопаснее.
Но это ведь просто не правда. Строчек кода столько же, "безопаснее" быть не может в силу полной эквивалентности семантики.
В useEffect переданная функция будет вызван асинхронно в отличие от componentDidMount.
В js любую синхронную ф-ю всегда можно вызвать асинхронно. А вот наоборот — нельзя. Так что синхронный интерфейс в подобных случаях более предпочтителен (тем более, что в 9 случаях из 10 хочется именно синхронной работы componentDidMount).
Но это ведь просто не правда. Строчек кода столько же, «безопаснее» быть не может в силу полной эквивалентности семантики.
строчек кода меньше.
В js любую синхронную ф-ю всегда можно вызвать асинхронно. А вот наоборот — нельзя.
в componentDidMount синхронный вызов будет откладывать отрисовку DOM. Иногда это правда надо, но из моего опыта как раз наоборот — 1 случай из 10.
но если нужен синхронный вызов то есть хук — useLayoutEffect
reactjs.org/docs/hooks-reference.html#uselayouteffect
строчек кода меньше.
Как меньше, если столько же? Они же 1к1 переводятся. Их не может быть меньше, по определению.
в componentDidMount синхронный вызов будет откладывать отрисовку DOM.
Так нам и не надо практически никогда рисовать дом, пока didmount не отработает. На самом деле, мне даже очень сложно представить случаи, когда нужно обратное поведение. Компонент ведь тогда находится в некорректном стейте, его by design нельзя рендерить в этом случае.
но если нужен синхронный вызов то есть хук — useLayoutEffect
Ну то есть это в итоге ничем не отилчается по возможностям от лайфцикл методо, кроме того, что методы — удобнее и проще в использовании.
В итоге та и остается непонятным, зачем кому-либо хотеть хоть в каком-то кейзе использовать хуки, если минусы — есть, а плюсов — никаких.
deleted
Запрос к API c React Hooks, HOC или Render Prop