Как стать автором
Обновить

Комментарии 27

Тоже пишу на React профессионально. Главный недостаток в нем для меня это постоянный ре-рендер. Это и побудило создать ему альтернативу.

Fusor - это как низкоуровневый React на минималках, с одним АПИ методом update и полноценным синтаксисом установки атрибутов.

В нем можно как вручную управлять реактивностью, так и автоматически с помошью любых паттернов: observable/signals...

Также недавно сравнивал сжатые размеры React~43kB и Fusor~3kB.

Есть же Vue, ну можно даже с jsx использовать

Какой смысл альтернативы, если под него ничего не написано

Ну как в таких случаях говорят - Just for fun, мне кажется. Написать альтернативу React - прекрасный челлендж, который позволяет в том числе и в том самом реакте разобраться.

В целом-то, альтернатив React формата "поменьше и покороче" вагон и маленькая тележка: Preact, Solid, вот Fusor тот же. Штуки интересные, но React предсказуемое и уже всем известное решение, с кучей библиотек на все случаи жизни. А альтернативы - сегодня они есть, завтра нет, ui-китов под них обычно не завезли, ну и всё прочее.

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

Кстати, у Fusor есть еще несколько киллер-фич, помимо простоты и наглядности, которых нет у других:

  • Выбор стратегии стейт менеджмента за вами. Можно использовать хоть обычные переменные там где это подойдет (много где подходит).

  • Возможность обновлять не только изменившееся место в DOM, но и делать это без DIFF всего дерева приложения от ROOT. Можно настраивать стартовые позиции DIFF от конкретных элементов и прерывать DIFF в нужных местах. Например компонент делает DIFF только для себя.

  • Также есть возможность выбора стратегии создания/обновления/DIFF компонентов. Например мы можем в нужных местах добавить асинхронность для больших списков или очередь.

  • Возможность применения функционального программирования, так как АПИ это PURE функции, нет контекста и связанных с ним сайд-эффектов (изменение DOM не влияет на это).

Какой смысл альтернативы, если под него ничего не написано

Какой смысл был создавать React, когда был backbone и jQuery?
Какой смысл был создавать Vue, когда был React?
Какой смысл был создавать Node.js, когда был PHP?
Ну и так далее, список можно до бесконечности продолжать...
С таким подходом мы бы дальше Assembler'a не продвинулись и дальше гужевых повозок, ведь зачем что делать, если уже есть Х и Y.

если под него ничего не написано

А что, теперь самому что-то написать это проблема? Если нет библиотеки/ui кита и т.п., то всё, приплыли?

Шикарный ответ. Без сарказма.

Наверное самая большая боль React-а - это его стейт-менеджмент. Начиная с уже упомянутого useEffect-а, до организации хранилищ. Можно использовать другой стейт-менеджер, но мало того что это притянет ещё доп вес к 135 КБ изначальным, так ещё и говорит о несостоятельности стейт-менеджмента самой библиотеки.

Вторая боль для меня - ререндеры. Собственно реакт на них базируется, но делает он их бездумно (эту проблему как раз пытается решить React Compiler). На самом деле, лишь изредка вас могут застать в расплох ререндеры, но сам факт того что ВСЕ функции и переменные постоянно создаются по новой просто бесит. А если изменить в хранилище что-то, то это вызывает каскадный ререндер всех внутренних компонентов даже там, где ничего из этого хранилища не используется. Это личная субъективная боль, у меня прям зудит в голове когда я думаю об этом.

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

Реренедеры могут быть болью, когда из-за них сильно проседает производительность или сбрасывается стейт. У меня таких случаев было не очень много, поэтому я в статью и не включил.

Хотя соглашусь, что сам факт довольно бесячий. И это по сути следствие использования функциональных компонентов и обходящего их недостатки дизайна хуков (тоже как минимум спорного). Я вообще когда впервые хуки увидел, подумал что это шутка какая-то, помнится, даже сказал тимлиду фразу "Хуки это костыль". Но костыль или нет, к нему привыкаешь. Да и самое ценное, что хуки позволяют вынести логику и переиспользовать её. Как инструмент для этого они хороши, как замена стейт-менеджеру... Ну спорно. Но надо понимать, что немало современных стейт-менеджеров без хуков бы и не появились или были бы куда менее удобны.

Так всё эти проблемы решены много лет назад, ещё с 2017 года или даже раньше.
Всё просто, React + MobX.
React для рендера, MobX для управления состоянием, как глобальным, так и локальным. И никаких тебе ререндеров лишних да и никакой боли в целом. Тупо наслаждаешься всеми благами JSX'a и компонентного подхода, а так же человеческим стейт менеджментом и минимальным кол-вом кода.

https://stackblitz.com/edit/vitejs-vite-dspjoj?file=src%2FApp.tsx&terminal=dev

В комментариях появились и уважаемый господин Карловский, и сторонники mobx. Значит, статья удалась, я счастлив)

По $mol_wire - я увидел какое-то огромное количество бойлерплейта и несколько странных решений типа превращения асинхронной функции в синхронную, а также дебаунс с помощью sleep (если этот sleep - задержка на некоторое время, как обычно, то это ж не дебаунс по идее, не?). Ну и в целом в готовый код на реакте это впилить будет сложно, тут на вид потребуется большая переделка всего и вся.

По Mobx - я пользовался им года 3 назад. После redux это, конечно, был глоток свежего воздуха, писать было гораздо проще и приятнее. Но вот глядя на пример приведённый - ну как-то многовато кода писать приходится, не находите? Каждый компонент со стейтом в observer запихивать, хуки использовать и тот же useEffect... А ещё у mobx огромный бандл в 60 Кб. А у Zustand, например, 1.2 Кб. И у Zustand простое и лаконичное API, которого в целом хватает. Может он и умеет не всё, что умеет mobx, но хватает ведь.

Флеймогонная тема, в общем)

я увидел какое-то огромное количество бойлерплейта

О какой бойлерплейте идёт речь?

sleep - задержка на некоторое время, как обычно, то это ж не дебаунс по идее

Дебаунс возникает из-за отмены предыдущей задачи во время этого sleep.

в готовый код на реакте это впилить будет сложно

Не попробуешь - не узнаешь.

Но вот глядя на пример приведённый - ну как-то многовато кода писать приходится, не находите?

Нет) Многовато это что? Слово observer и makeAutoObservable?) Потому что кроме этих 2х ребят всё остальное по сути просто тупо нативный JS код.

Каждый компонент со стейтом в observer запихивать

И что?) Зато оптимизация производительности из коробки и никаких лишних перерендеров. Можно написать плагин для vite/webpack чтобы автоматом компоненты в observer заворачивались, я так и сделал несколько лет назад)

хуки использовать и тот же useEffect...

Поясните что вы имеете ввиду

А ещё у mobx огромный бандл в 60 Кб

Это вы про 17.5kb gzip?) Ну в масштабах самого реакта и вообще real world проектах, это копейки и они тонут в общем размере проекта) Так что как обычно по поводу размера, аргумент не аргумент
https://bundlephobia.com/package/mobx@6.13.7

И у Zustand простое и лаконичное API, которого в целом хватает

Простое, да. Но не лаконичное, ибо там всё в ручную надо делать. И писать много бойлерплейта и лишнего кода. А в mobx автоматические подписки/отписки.

Вот например лишний код, и это минималистичный и безобидный пример:

А если избавиться от лишнего мусора в виде useAppStore, то будут лишние перерендеры всей подноготной со вложенными компонентами почем зря, прям как в реакт или редакс.

Вот ссылка:

https://stackblitz.com/edit/stackblitz-starters-ab7az7ym?file=src%2FApp.tsx

Так что трезво взвесив всё, из популярных решений по сей день нет ничего лучше MobX'a для реакта. Более того, когда используешь MobX, по сути нет разницы какая версия реакта, 16, 17, 18 вообще пофиг.

Поясните что вы имеете ввиду

Я про это. Возможно, это просто утрированный пример и можно сделать элегантнее. Я на mobx писал давно и недолго, в формате "написал и забыл", так что докапываться к нему сильно не буду)

То, что mobx позволяет так делать - это плюс, конечно, это выглядит красиво и удобно. В принципе, интересно будет потыкать в будущем при возможности.

Я про это. Возможно, это просто утрированный пример и можно сделать элегантнее

Это специальная демонстрация функции reaction, не более. Что с помощью нее можно трекать изменения конкретных свойств и реагировать на них. В данном примере она не несет никакой пользы, просто в консоль пишет, что локальный title или глобальный title были изменены) Опять же, чисто для демонстрации принципа)

о, что mobx позволяет так делать - это плюс, конечно, это выглядит красиво и удобно

Это ещё цветочки, по сути там целый простор для всяких шикарных штук. Можете сделать мега удобные классы для работы с формами, валидаторы, роутер, да и вообще все что угодно, ограничений то нет) Только фантазия) У меня уже давно 100500 решений написано и вспомогательных штук на все(99%) случаи жизни) Для меня разработка проходит вообще играючи, никаких проблем, ни с чем не надо бороться, ничто не мешает и не вставляет палки в колеса.

У меня уже давно 100500 решений написано

Вы же их заоупенсорсили, чтобы другим такой ерундой страдать не пришлось?

Вы же их заоупенсорсили, чтобы другим такой ерундой страдать не пришлось?

Не, они создавались для себя, а не для всех)

То есть вы серьёзно всем предлагаете вместо автомобиля, брать моток проводов и раму от багги, и самим в гараже на их основе собирать свой уникальный драндулет, потому что вы так сделали и вам ваш уникальный ДеЛориан нравится, но вы его никому не покажете? Прекрасный совет!

Я говорю о том, что, то что сейчас есть из готовых "решений", далеко от идеала и можно сделать самому значительно лучше, причем предела для улучшений нет. Но это не для всех. Для меня например нет проблем, если то, что есть из готового мне не нравится, я просто делаю сам и всё. Это ведь мне надо, а не кому-то ещё прежде всего) Я конкретно себе упрощаю жизнь в первую очередь) Но опять же, именно для разработчиков. я ничего нового не говорю, это вообще типичная практика. А для промысловиков которые решают любую задачу готовой библиотекой, а если не находят ее, то встают в ступор, увы их ах. Это не для них) Они гордо называет тех, кто легко решает свои проблемы велосипедистами) Думая таким образом, что они якобы выше уровнем как разработчики))

Меня очень интересует ваше решение для работы с формами. Я пишу много форм и это моя боль, не могли хотя бы рассказать свою идею?

На мобх писал мало, думаю присмотреться к нему внимательнее.

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

https://stackblitz.com/edit/vitejs-vite-naqjwa41?file=src%2FApp.tsx&terminal=dev

несмотря на указанные ниже проблемы, я до сих пор считаю React прекрасным и удобным инструментом для создания фронтенда

Тут то ли стокгольмский синдром, то ли синдром утёнка.

Сам по себе React занимает буквально меньше 7 Кб!  Это даже меньше preact (~ 11 Кб), который заявляет себя легкой альтернативой. Так что получается, врут о том, что React большой и прожорливый? Но что за оставшиеся 137 Кб...

React: Bundle size is 185 kB -> 58.2 kB (gzip)
Vue: Bundle size is 94.7 kB -> 37.9 kB (gzip)
Preact: Bundle size is 22.7 kB -> 8.61 kB (gzip)
$mol_jsx_view: Bundle size is 20.2 kB -> 6.11 kB (gzip)

вы получите пустоту только на месте, в котором должен был быть компонент. Остальное приложение продолжить корректно работать. ТАК И ДОЛЖНО БЫТЬ! ПО УМОЛЧАНИЮ!

Не должно быть никакой пустоты. Должно быть нормальное сообщение об ошибке. И это уже должно быть поведением по умолчанию, а не то, что вы написали.

веселит тот факт, что eslint при помощи определённых правил может определить нехватку каких-то зависимостей у хука, а сам React нет.  Хотя тут я немного лукавлю - уже упомянутый React Compiler вроде бы научился где надо подставлять useCallbackuseMemo и memo

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

Несмотря на перечисленные недостатки, свою работу React делает и делает её хорошо - тысячи прекрасно работающих приложений и огромная экосистема тому подтверждение.

Я надеюсь это сарказм такой? Что ни приложение на Реакте, то тормозной глюкодром. Яркий пример - интерфейс ВК, где лучшие разработчики России не смогли совладать даже с консистентным управлением стейтом, в результате чего редактирование одного поста в отложенном заменяет другой до перезагрузки и тп приколы.

Тут то ли стокгольмский синдром, то ли синдром утёнка.

Оба два, наверное)

Я надеюсь это сарказм такой? Что ни приложение на Реакте, то тормозной глюкодром. 

Это, наверное, стадия принятия) Но насчёт "тормозного глюкодрома" - как по мне, преувеличение и оценочное суждение. Есть категория людей, у которых вообще любой JavaScript - "тормозной глюкодром". А мне, например, на большинстве сайтов, норм. Всё относительно.

React позволяет быстро вкатиться и быстро разработать. И почти всегда "быстро" - антоним слова "хорошо".

быстро вкатиться и быстро разработать

Это иллюзия, как с айсбергом: тебе показывают hello world и ты думаешь, что уже вкатился, не зная, сколько ещё работы предстоит, чтобы довести решение до прода: от ручной установки "крутилок" и обработчиков ошибок, до прикручивания локализации и надёжной синхронизации роутера с урлом.

Ну а дилемма между "быстро" и "хорошо" - это признак плохого инструмента. Хороший делает "быстро и качественно". Ручной пилой можно либо быстро резать, но некачественно, либо качественно, но медленно. А вот электролобзик сделает то же самое и быстро, и качественно.

Раздражает useCallback и массив зависимостей в хуках. И перерендер ВСЕХ подписчиков контекста

Хорошо, хоть сами разработчики React признают, что «it’s probably not the ideal user experience».

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

в JSX-разметку нельзя передавать объект, это вызовет ошибку рендера (возможность просто вывести этот объект как JSON опять же не влезла в 135 Кб React-DOM, видимо)

Это честнее и правильнее относительно нативного жс. Вроде наоборот все ноют о том, как реакт непонятен и его механизмы работают как магия - так зачем еще одно заклинание добавлять? Хотите отобразить объект? Ну так скажите каким образом, так как это не JSX элемент, а преобразовывать его автоматически в строку JSON - не всегда то, что нужно (промолчку про дурной тон дебажить через отображение объекта в интерфейсе).

Разгадка проста: React полагается на порядок вызова хуков! Иными словами, состояние хуков внутри хранится в строго определённом порядке. Если порядок изменится, то состояние (скорее всего) станет невалидным. Поэтому хук вызывается всегда, нужен ли он или нет.

Не кажется ли вам это костылём?

Не знаю откуда источник этой разгадки, но хуки используются только в верхнем уровне по причине того, что они взаимодействуют с потоком рендера компонента (и в функциональных компонентах поток рендера описывается как раз на самом верхнем уровне), следовательно, вне этого потока не может быть никакого представления о таком концепте, как "поток рендера" - ни в функциях, ни в условном коде, ни в методах класса, ни где-либо вне компонента (в частности его самого верхнего уровня) в принципе. А порядок хуков уже имеет вторичное значение (а вообще лучше бы писать код так, чтобы порадок хуков не влиял ни на что).

Или необходимость вручную указывать зависимости хуков в массиве? Нет, в этом есть какой-то шарм, но...  Это выглядит как ручная правка убогости самого механизма встроенных хуков!

Лучшее, что есть в функциональной парадигме реакта - подписываешься на изменение параметра (состояния), и выполняешь желаемый алгоритм, надо просто аккуратно это делать и не очень увлекаться добавлениями эффектов. Жаловаться на это примерно как жаловаться на addEventListener. В целом useEffect сложная штука для восприятия только потому, что сам рендеринг в реакте сложно устроен, ввиду использования функциональной парадигмы программирования, от чего у ООП любителей мозг кипит (отсюда и претензии к пересоздаванию функций при каждом рендере, нелюбовь к useCallback/useMemo и тд).

Добавлю про недостатки:

  • Низкая скорость разработки на старте. React не фреймворк, а скорее конструктор фреймворков. Надо подключить и настроить с десяток-другой библиотек, написать обертки для компонентов форм, чтобы упростить и ускорить работу с библиотекой вроде React Hook Form.

  • Порог вхождения в экосистему React стал довольно большим.

  • Вместо того, чтобы использовать общее api для глобального  и локального состояния, у нас теперь для каждого вида состояний появилось свое api - useState, менеджер состояний или контекст, библиотека для форм, библиотека для кэша с бэка.

  • Хуки добавляют больше проблем, чем решают. Переиспользовать логику можно и на объектах с меньшей болью. Наследование для этого не нужно. Сами классы не плохи, а вот реализация компонентов на классах плоховата.
    Вместо привязки переменных к this в компоненте и привязки функций к this через auto bind или стрелочные функции теперь на каждый вызов компонента создаются новые функции и значения. Оборачивание их в хуки делает код компонента менее читаемым. А если забыть обернуть, можно получить баги или проблемы с производительностью. useMemo нужно использовать с умом, т.к. излишняя мемоизация тоже может приводит к снижению производительности. Еще надо не забывать, что передача неизменившихся пропсов, обернутых в useMemo и useCallback или useRef, без обертки компонента в React.Memo не избавит от лишних рендеров.
    useEffect теперь практически антипаттерн, но об этом уже написано в статье.

  • Для гибкости и чистоты кода логику стоит отделять от отображения. В классовых компонентах этого не делалось, но там хотя бы jsx писался в отдельной функции в компоненте. Сейчас же в одной функции пишется и логика и jsx.

  • Лично мне нравиться включать и отключать дополнительную переиспользуемую логику компонента через директивы, но их нет в React-те.
    Вместо этого есть хуки, которые обычно намертво прибиты к компонентам.

  • Такие вещи как React Query сделаны на хуках. Получился не один, а два источника данных (стор и кэш), которые зачастую нужно объединять. Теперь обмен данными между стором и api обычно делают не напрямую, а через view слой. Чаще всего через useEffect, добавляя лишние обновления компонента и запутывая код. React Query хуки обычно вызывают не в одном месте, а в каждом компоненте, где требуются данные с бека, тем самым нарушая консистентность и теряя прозрачность взаимодействия фронта с беком.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории