Pull to refresh

Comments 80

Всё меняется. Правила игры меняются. Это бизнес. Как там фраза знаменитая звучит: «Меняйся или сдохни» — фраза из фильма «Человек, который изменил всё» (Moneyball, 2011)

Ох мой комментарий не понравился) извините

16 лет просто...

Меня в Реакте ужасает только forwardRef. Остальное в принципе съедобно.

Рендер — это не рендер

Чего? Вы сейчас серьёзно? Рендер не рендер только потому, что Вы его явным образом вызвать не можете? Он вызывается периодически при каких-либо изменениях states/props в компонентах (ну и при перерисовке родительских компонентов и других, в принципе разумных ситуациях).

И какой именно React.js Вы ненавидите? Используете классовые компоненты, которые считаются уже довольно таки сильно устаревшими. У них есть много других проблем (например, разница между React.Component и React.PureComponetn), но то, что функцию рендера нельзя явным образом вызвать - не проблема, а механизм работы самого React.js

Ну почему?! О чём они думали? Почему вызов метода render не приводит к рендеру? Это же так очевидно!

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

С какой легкостью мы бы подключали внешние источники данных!

Да, в общем-то, и сейчас это делается не трудно. Взял вывел в отдельный контейнер определения источников данных и методов для работы с ними и всё. Например (Redux, connect):

const mapStateToProps = (state, ownProps) => {
	const { database } = state;
	return {
		database: database,
	};
}
const mapDispatchToProps = (dispatch) => {
	return {
		getData: (ids) => dispatch(getDataDispatch(ids)),
	}
}

export default connect(
	mapStateToProps,
	mapDispatchToProps
)(MyComponent);

// MyComponent - какой-то классовый компонент

И ничего плохого в этом нет. Работает, даже на продакшене. Бывают конечно косяки, но где их нет?

Это всё. Зачем целая директория для этого пакета?

Коротко - чтоб Вы спросили :) А если серьёзно, какая разница какой у них код-стайл? Да потребовалась целая директория, для такого маленького пакета, ну и что? А ничего. Работает этот код в отдельной директории или нет - всё равно, главное чтобы он решал свою задачу. Да и, есть вероятность, что кэш импортов здесь как-то участвует (мб что-то оптимизирует), но не уверен.

Какова вероятность, что, обнаружив какой-то баг, я смогу попробовать его исправить и отправить PR, а не ждать и надеяться, что мейнтейнеры когда-нибудь сделают это сами? Около нулевая. Именно поэтому в репозитории React'а висят баги от 2014 года — не потому, что их кто-то другой не может исправить, а потому что это фиктивный open-source. Это системная непрозрачность, и она прослеживается во всём, что делает React.

Почти со всем согласен. Это печальная ситуация.

Как? Как можно было принять такое решение? О чём они думали? Это же абсурд! Я понимаю, что разработчики React'а ненавидят ООП, но не до такой же степени?

Чёрт... если говорить о хуках, то это вообще в целом функциональщина JS. Тут нет ООП, тут есть функциональное программирование на JS. Какой-то... надуманный минус. Не лаконично, согласен, но хуки не рассчитывались на ООП. Собственно, Вы их и в классовых компонентах без обёрток (костылей) использовать не сможете.

Но зачем нужна обёртка, если у событий единый стандарт? React застрял в 2010-х, хотя вышел в 2013. Его «нормализация» событий — это попытка решить проблемы IE8 и старых Firefox, которые давно умерли? Нет ни одной причины иметь SyntheticEvent, кроме как сделать код для React'а несовместимым с чем-то другим.

Я думаю, что это было необходимо из-за особенностей механизмов самого React.js. Да и обязан ли он быть кому-то "библиотекой, с которой можно перенести всё на Solid.js"? Не думаю. Обработчики синтетических событий подхватывают изменённые состояния, в то время как обработчики обычных событий (без специфической обёртки) - нет. Об этих особенностях я даже писал статью в своё время.

React.js не обязан быть тем решением, которое можно перенести на Solid.js, Angular или другие фреймворки/библиотеки. Это... странно :)

Но нет! Compiler намертво закеширует значение, полученное при первом обращении к MyOwnObject.uniqueId, и никогда больше к нему не обратится!

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

А сейчас этого не происходит и это хорошо. Постоянные пересоздания объектов и так мешают React.js быть более эффективным в контексте аллокаций памяти.

React больше не про разработчиков. Он про контроль.

Не согласен. Любая библиотека или фреймворк сопровождаются своими ограничениями. По не эффективной работе с памятью в React.js - согласен (но и тут нужно доп. проверки и исследования), а про остальное... как-то всё надумано и по верхам. Синтетические события и ключевые слова по типу className - это скорее необходимость, чем "ааа, нас лишают контроля! мы не сможем React.js переписать на Solid/Vue/Angular за день!!". Да и весь JSX код всё равно компилируется в JavaScript, а в DOM всё также добавляются div'ы c class, вместо className.

И пока мы молча принимаем его правила — он будет становиться всё более оторванным от реальности.

Чушь конечно. Боюсь представить какая выйдет статья, если Вы по программируете на Angular пару лет :)

Поэтому я ненавижу React.

А мне он очень нравится, также как Solid.js и чистый JavaScript. При правильной работе с ним можно добиться очень неплохого результата.

Коротко - чтоб Вы спросили :) А если серьёзно, какая разница какой у них код-стайл? Да потребовалась целая директория, для такого маленького пакета, ну и что? А ничего. Работает этот код в отдельной директории или нет - всё равно, главное чтобы он решал свою задачу. Да и, есть вероятность, что кэш импортов здесь как-то участвует (мб что-то оптимизирует), но не уверен.

Есть ощущение что это как-то связано с react-native и cерверными компонентами

Он вызывается периодически при каких-либо изменениях states/props в компонентах (ну и при перерисовке родительских компонентов и других, в принципе разумных ситуациях).

Написали бы сразу – вызывается непредсказуемо.

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

Потрясающе предсказуемо. Тошнит уже от этих «механизмов» реакта.

Вы бы хотели вызывать функцию рендера?

Да. У меня есть какой-то JSX, в нем обращение к каким-то свойствам, какие-то условные конструкции – не важно. Просто пусть метод render отрендерит результат этого JSX выражения. В чем проблема?

export default connect( mapStateToProps, mapDispatchToProps)(MyComponent);

Уже даже сам автор этого недоразумения отказался от своего детища, а вы продолжаете.

И ничего плохого в этом нет. Работает, даже на продакшене.

Это сильно, конечно!

А если серьёзно, какая разница какой у них код-стайл?

Почти со всем согласен. Это печальная ситуация.

Вы же себе прямо противоречите.

если говорить о хуках, то это вообще в целом функциональщина JS. Тут нет ООП, тут есть функциональное программирование на JS. 

Я описал в статье сколько это на самом деле стоит.

Я думаю, что это было необходимо из-за особенностей механизмов самого React.js. Да и обязан ли он быть кому-то "библиотекой, с которой можно перенести всё на Solid.js"? 

Он обязан не ломать браузерное API.

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

Что? Это как? Как вызов функции может привести к пересозданию какого-то внешнего конфигурационного объекта? Вы действительно считаете, что сломать базовое поведение JavaScript где обращение к геттеру приводит к его вычислению – это нормально?

Постоянные пересоздания объектов и так мешают React.js быть более эффективным в контексте аллокаций памяти.

Так может вы не будете защищать хуки? Это ведь они к этому приводят, и им приходится чинить их еще больше ломая все?

как-то всё надумано и по верхам

По верхам? Ну ладно.

Я описал в статье сколько это на самом деле стоит.

Справедливости ради, созданные функции даже не успеют попасть в old space, как их грохнет GC. А объекты созданные на каждый рендер тоже легкие, т.к. у них стабилизированный shape.

У реакта куда больше проблем с перфом, чем вами описанные

1) Ужасно долгий renderToString на SSR, который не даст превысить 40-50 RPS на среднем по производительности ядре серверов.

2) VDOM дерево куда сильнее бьет по GC, т.к. там не всегда стабильная структура и очень много лишних созданий shape'ов.

3) Громадный бандл, это дорого по компиляции/парсингу.

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

Поэтому, когда я хочу писать быстрые приложения, но оставаться в экосистеме реакта (по крайней мере, чтобы найм не усложнить) я использую Preact+Signals. Сильно проще выполнение diff, т.к. глубина дерева сигналов меньше чем vdom, сильно меньше бандл, плюс базовая скорости выше, чем в реакте.

Так может вы не будете защищать хуки? Это ведь они к этому приводят, и им приходится чинить их еще больше ломая все?

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

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

Написали бы сразу – вызывается непредсказуемо.

Ну, я же не буду врать :)

Тошнит уже от этих «механизмов» реакта.

Ну так используйте другую библиотеку/фреймворк.

Просто пусть метод render отрендерит результат этого JSX выражения. В чем проблема?

А путь метод не рендерит результат этого JSX выражения при явном вызове render. В чём проблема?

Уже даже сам автор этого недоразумения отказался от своего детища, а вы продолжаете.

Это говорит мне человек, который до сих пор классовые компоненты использует и "искренне" не понимает, почему "хуки не поддерживают лаконичность и удобность ООП". Ага, да.

Вы же себе прямо противоречите.

Прям бесит, когда чел вырывает фразу из контекста и делает вывод по всей статье. Я согласился не с вашими предъявами к рендеру или ООП в хуках - я с другим согласился. Авторам React.js и мейнтейнерам есть над чем поработать в плане общения с пользователями их библиотеки. Вот с этим я согласен, а не со всем, что вы в статье пишите. Вы вроде цитаты умеете соотносить с ответами, так в чём дело?

Я описал в статье сколько это на самом деле стоит.

Ага. На самом деле. Да, конечно. Статистику конкретную я не увидел.

Он обязан не ломать браузерное API.

Эмм... браузерное API? Которое в разных браузерах может отличаться? И React.js его сломал? Хм... ну, это конечно сильно. Если взгляните на какие-нибудь новомодные JS фичи то не все современные браузеры их поддерживают. API у всех разное, кто-то даже движок использует разный, представляете? А значит оценивать работу React.js на этом "своём" движке нужно учитывая его особенности.

Ничего React.js не ломает в контексте браузерного API. Вам показалось :)

Вы действительно считаете, что сломать базовое поведение JavaScript где обращение к геттеру приводит к его вычислению – это нормально?

Ну так... может ну его, этот геттер? :) Зачем он тут нужен? Просто добавили чтобы показать как компилятор React.js захэширует его вызов. Причём сама техника геттеров может успешно никогда вообще не использоваться React.js разработчиками.

А вообще... я буквально только что решил проверить ваш этот пример с геттером и вот что обнаружил - он нормально работает в React 19. Можете и сами попробовать хотя бы и через браузерное решение:

import React, {useState} from "react";

const MyOwnObject = {
  get uniqueId() {
    return Math.random()
  }
}

export default function App() {
  const [count, setCount] = useState(0);
  const up = () => setCount(Math.random())

  return (
    <div>  
      <div>Current count {count}</div>
      {/* "Чудо чудное" MyOwnObject.uniqueId изменяется! */}
      <div>Current unique key {MyOwnObject.uniqueId}</div>      
      <button onClick={up}>Update</button>
    </div>
  )
}

Возможно у вас какие-то свои настройки компилятора установлены. Делаем вывод - возможно вам пора обновить ваш React.js :) Ибо проблемы в новой его версии нет. Возможно и в React.js 18 её нет, я не знаю какая у вас версия. Всё работает нормально. Работает точно так же, как если бы мы подставили вместо MyOwnObject.uniqueId Math.random(). Проверяйте тщательнее свой программный код и не вводите людей в заблуждение :)

Так может вы не будете защищать хуки? Это ведь они к этому приводят, и им приходится чинить их еще больше ломая все?

Да-да-да... виноваты хуки, классы и ещё до кучи. Виноват JavaScript! О... может быть виноват объект Function? Точно! Наверняка асинхронные генераторы, из-за них потом приходится всё чинить!

Нормально делай - нормально будет :) Нет идеальных инструментов для решения прям всех задач. Не одним React.js едины, есть ещё Solid.js, Vanilla JS, Angular, Ember, и до кучи библиотек/фреймворков - копай сколько хочешь. У всех разные подходы, интересные особенности.

Если проблема реально есть - хотелось бы в ней действительно разобраться, а не бросаться громкими лозунгами по типу "React.js плохой! Ломает браузерное API! JavaScript поведение не по документации! Компилятор плохой, потому что геттер не вычисляется!". Это крайне не профессиональный подход.

По верхам? Ну ладно.

Ну да.

А вообще... я буквально только что решил проверить ваш этот пример с геттером и вот что обнаружил - он нормально работает в React 19. Можете и сами попробовать хотя бы и через браузерное решение:

Проверяйте тщательнее свой программный код и не вводите людей в заблуждение :)

Я проверил очень тщательно, а вы нет.

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

Если по ней перейти, то можно увидеть следующее:

1) Установлен React последней версии, и самое главное – бабель плагин, без которого компайлер не работает.

"dependencies": {
    "babel-plugin-react-compiler": "^1.0.0",
    "react": "^19.2.0",
    "react-dom": "^19.2.0"
  },

2) Бабель плагин подключен в конфиге сборщика, и поэтому работает!

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react({
    babel: {
      plugins: ['babel-plugin-react-compiler'],
    },
  })],
})

3) Это песочница. Вы можете посмотреть код и понажимать на кнопочку, чтобы убедится, что геттер не работает:

https://stackblitz.com/edit/vitejs-vite-px9smmwy?file=src%2FApp.tsx

Вы же предоставили ссылку на пустую песочницу где ничего нет:

То, что эта песочница называется Online React Compiler, вообще не значит, что там там используется React с включенным компайлером.

Остальное комментировать неинтересно.

Я проверил очень тщательно, а вы нет.

Какой вы молодец :) Тогда без явно включенного компилятора тоже имело смысл бы проверить и добавить в статью, что вот без него - всё вполне работает. Мб тут есть баг, и есть смысл о нём команде React.js сообщить.

Вы же предоставили ссылку на пустую песочницу где ничего нет

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

используется React с включенным компайлером

Справедливо. Его действительно нужно явным образом включать. Тут я ошибся. Вы же не сам React.js за это "минусовали", а только лишь за компилятор с babel-plugin-react-compiler и vite (исходя из сборки).

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

В любом случае странно, что без явного определения компилятора всё работает корректно (ссылку на редактор и код для проверки я оставил в комментарии выше), а с включённым компилятором - нет. Вероятно тут проблема комплексная.... где-то явно есть баг, который не полечили.

Остальное комментировать неинтересно.

Ну... эм... никто и не просил, собственно :))

К слову, тут можно проверить ещё и Webpack или Rollup (как с ними ведёт себя React Compiler). Вдруг тут эта проблема отсутствует... До "тщательной проверки" тут ещё далеко.

репозиторий React'а — это лабиринт, и для навигации там нужен путеводитель

К реакту отношусь, мягко говоря, так себе – по совершенно другим причинам, чтобы рассказать, понадобилась бы отдельная статья. Но вот заглянув как-то в его репо, был приятно удивлён, в кои-то веки увидев хорошо структурированный код. Сейчас у меня две основные технологии: РНР и флаттер. И там, и там файл в 5000 строк лапши – обыденность. Это лучше? Не понимаю, как они в этом разбираются, и как можно туда что-то законтрибутить свежему человеку. Какое-то безумие: предполагается, что цель разработчиков и РНР, и флаттера – помогать программистам писать хорошо структурированный код, но при этом сами они этого делать не умеют.

Хорошо структурированный код - это не только разбиение по семантическим папкам (пакетам) и отдельным файлам со связанной логикой и заботой о переиспользовании. Это еще и удобный DX в виде быстрых переходов на место объявления. Здесь же чтобы попасть в нужное место приходится делать десяток быстрых переходов, причем иногда они динамические и могут быть не связаны типами, и приходится делать "поиск по всем репозиториям". Другими словами - "обертка на обертке с названиями переменных в 1 символ".

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

Конечно, 5000 строк лапши - это никуда не годится, но и с реактовым подходом тоже не все гладко, и можно было бы сделать лучше.

Ну это вы еще spring boot не видили, там любой эксепшен в hello-world это 100 уровней вложенности. Такая цена вот гибкости, не всем походит, мы перестали например много где srping-boot использовать из-за этого

Насчёт спринга: да, стактрейс там очень немаленький, но на моей практике найти причину ошибки труда не составляло. В IDE сразу показывается, на какой строчке кода произошло падение (почти всегда), на полигонах и проде стактрейс хоть и в виде файла/лога в кибане, но и его можно как-то обработать, да и порой хватает глазами секунд 10 пробежать стактрейс для поиска "своего" кода. У спринга есть ряд проблем, но мне тяжело представить, чтобы причиной перехода к другому фреймворку были именно стактрейсы

Проблема не в том что в них тяжело разобраться, проблема в том что при такой глубине стека многие оптимизации JVM перестают работать и условно можно получить сервис который обработает в два раза меньше запросов да еще и с большой latency

Автор не знает что существует поиск, я нашел сорцы useSyncExternalStore за минуту прямо не выходя из браузера: https://github.com/search?q=repo%3Afacebook%2Freact useSyncExternalStore&type=code

Еще автор не знает про то, что реакт это не только замена jquery, но очень хитрый рантайм, у которого нет аналогов https://overreacted.io/progressive-json/

Автор не знает что существует поиск, я нашел сорцы useSyncExternalStore за минуту

Приведите код, пожалуйста, а не ссылку на результаты поиска.

но очень хитрый рантайм, у которого нет аналогов

Смешно.

Приведите код, пожалуйста, а не ссылку на результаты поиска.

Третья ссылка в результах код клиента https://github.com/facebook/react/blob/eb2f784e752ba690f032db4c3d87daac77a5a2aa/packages/use-sync-external-store/src/useSyncExternalStoreShimClient.js#L25

Четвертая код сервера https://github.com/facebook/react/blob/eb2f784e752ba690f032db4c3d87daac77a5a2aa/packages/use-sync-external-store/src/useSyncExternalStoreShimServer.js#L10

Смешно

Приведите список аналогов RSC

Третья ссылка в результах код клиента

Нет. Это не реализация useSyncExternalStore, а «обманка» которую можно использовать вместо реального useSyncExternalStore в реакте ниже 18-ой версии.

Четвертая код сервера

Ничего общего с реализацией это не имеет, код бы хоть посмотрели.

Ну да, я не вчитывался. Если все-таки посмотреть на сорцы, то аж на третьей страницы после всяких devtools и tests есть такой результат, который как раз похож на реализацию https://github.com/facebook/react/blob/eb2f784e752ba690f032db4c3d87daac77a5a2aa/packages/react-reconciler/src/ReactFiberHooks.js#L3941

Приведите список аналогов RSC

Я так понимаю кина не будет?

Какова вероятность, что, обнаружив какой-то баг, я смогу попробовать его исправить и отправить PR, а не ждать и надеяться, что мейнтейнеры когда-нибудь сделают это сами? Около нулевая. Именно поэтому в репозитории React'а висят баги от 2014 года — не потому, что их кто-то другой не может исправить, а потому что это фиктивный open-source. Это системная непрозрачность, и она прослеживается во всём, что делает React.

А в линукс ядре висят баги с 2012 и то потому что судя по всему поиск дальше не идет https://bugzilla.kernel.org/buglist.cgi?bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&limit=0&order=changeddate%2Cpriority%2Cbug_severity&query_format=advanced

В Реакте объединили управление ВИД-ом и СОСТОЯНИЕ-м в одиом флаконе. И от этого все мучения - хуки=хаки и ре-рендеринг. Которые команда Реакт пытается преодолеть все эти годы: batching, memoization, concurrency, SSR, RSC, compiler...

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

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

const ClickCounter = ({count = 0}) => (
  <button click_e_update={() => count++}>Clicked {() => count} times</button>
);

Если так хочется разделить - делаешь хук к компоненту в отдельном файле, делов то.

К реакту можно много где домотаться, но статья - вообще мимо. Человек хочет ангуляр просто.

но статья - вообще мимо

Я описал по пунктам все свои претензии, привел аргументы, ссылки, примеры кода и даже в некоторых случаях предложил альтернативу. Какие у вас контр-аргументы кроме «вообще мимо»?

Человек хочет ангуляр просто

А вы всегда придумываете позицию за оппонента, причем максимально нелепую? Статья про недостатки конкретного инструмента, а не про сравнение разных инструментов, и если вы выдаете свои домыслы за мою позицию, то может их стоит хотя-бы чем-то подкрепить?

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

  • В Preact'е нормальные события а не SyntheticEvent.
    Для регистрации обработчиков событий в Preact используется стандартная для браузера функция addEventListener, что означает, что именование и поведение событий в Preact работает так же, как и в обычном JavaScript / DOM.

  • В Preact'e нормальные аттрибуты в JSX – class, а не className, for а не htmlFor и пр., но при этом поддерживаются оба варианта, то есть можно и className. Это потому, что Preact полностью совместим с React.

  • В Preact'e полностью и без каких либо ограничений поддерживаются веб-компоненты.

  • В Preact'e нет компайлера ломающего поведение JavaScript, просто потому, что он там не нужен. Он существенно производительнее React'a + сильно меньше по размеру бандла + потребляет меньше памяти.

При этом в Preact'e, как и в React, есть функциональные компоненты и классовые, можно использовать то, что больше нравится. Есть все реактовские хуки – пользуйтесь если нравится. Есть сигналы, если хуки не нравится, и разумеется есть возможность подключить какой-угодно стейт-менеджер. Так что нет, я не хочу ангуляр, я хочу адекватный React, и пока на эту роль больше всего подходит Preact.

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

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

Не спорю, все так. Для себя я разработчиков делю, условно, на две категории – инженеры и ремесленники. Инженера найти трудно, ремесленника нет. Реакт разработчик для меня скорее ремесленник.

А что такое хороший разработчик и с чем он хочет связываться?

Всё-таки `click_e_update` сочетает в себе и обработку DOM-события, и указывает на необходимость обновления DOM, потому что изменилось состояние. То есть view даже в этом примере должен знать, что сделалось с состоянием.

Это по сути синтаксический сахар для click_e={(event, self) => {count++; update(self);}}. ВИД ничего не знает, он просто обновляется на каждый клик. Вот тут описано подробнее про этот модификатор.

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

Вот пусть у нас три компонента: некое поле со значением, кнопка обновления и компонент приложения, который всё это объединяет. Пример на Solid, но концептуально аналогичное можно написать и на Vue, и на $mol.

https://playground.solidjs.com/anonymous/fe3e5521-36f5-43f1-aa04-bd913e89e253

Скрытый текст
import { render } from "solid-js/web";
import { createSignal } from "solid-js";

function createCounter() {
  const [count, setCount] = createSignal(1);
  const update = () => {
    setTimeout(() => {
      setCount(value => value + 1);
    }, 1000);
  };

  return { count, update };
}

function App() {
  const { count, update } = createCounter();

  return (
    <div>
      <Dashboard count={count} />
      <ControlPanel onUpdate={update} />
    </div>
  );
}


function Dashboard(props: { count: () => number }) {
  return (
    <div>
      <p>Updated {props.count()} times</p>
    </div>
  );
}

function ControlPanel(props: { onUpdate: () => void }) {
  const handleClick = () => {
    props.onUpdate();
  };

  return (
    <div>
      <button type="button" onClick={handleClick}>Update</button>
    </div>
  );
}

render(() => <App />, document.getElementById("app")!);

Важно, что ни Dashboard, ни ControlPanel не знают ничего о работе друг друга и о потоке данных в приложении. При этом можно убедиться, что DOM обновляется строго атомарно: только нода со значением.

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

И на что же будет похож мой пример с вашей библиотекой?

Тут простейший пример

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

код становится проще в разы

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

так на чем писать фронт сейчас для своих поделок, чтобы все летало и быстро несложно было сделать?

Попробовав все что сейчас есть из устоявшегося, считаю что это либо VUE, либо Svelte.

VUE из-за зрелого коммьюнити с инфраструктурой, да и в целом отличного и модульного подхода (в тч composition API), кроме того найти разработчиков под него не проблема. Писать на нем проще, нет такого оверхэда как на React и Angular, компоненты просты и с нормальным HTML в качестве шаблона, причем в компонет можно как инкапсулировать все - и шаблон, и логику и стили, так и вынести их вовне. Отсуствие ада коллбэков, нормальная событийная модель и отсутствие JSX из-коробки (хотя можно использовать) для меня огромный плюс.

Svelte из-за простоты, размера и скорости, приятного и простого апи.

Все остальное или переусложнено, имеет странный api, необъяснимую магию или имеет околнулевое коммьюнити и полное отсутствие кадров на рынке.

Astro + Svelte (если фреймворк вообще нужен) вполне покрывают все задачи

На чем угодно, везде есть компромиссы. Вообще не надо забывать, что все эти фреймворки созданы для того, чтобы унифицировать подход к разработке и упростить взаимодействие с dom-ом, систему за вас никто продумывать не будет, хотя в ангуляре плюс/минус есть определенный системный подход к разработке, но «несложным» я бы его не назвал для начинающих. Реакт - это что-то из разряда собери сам, большая экосистема, но в тоже самое время для кого-то это минус, так как в ней легко утонуть и нет каких-то стандартных инструментов, которые рекомендованы для того же роутинга или клиентского кэша, плюс нет реактивности из коробки. Сейчас реакт обновил документацию и она стала в целом гораздо лучше для обучения. Вью - хорошая документация, реактивность из коробки, это фреймворк, а значит в коробке есть роутинг и стейд менеджер. Для себя я бы писал или на вью или на реакте. Для поиска работы в будущем, на реакте.

Для поиска работы в будущем, на реакте.

По какой причине вью для работы не рассматриваете? Не верите в его рост на рынке?

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

Я выбираю htmx + alpine по дефолту. Если понадобится фреймворк - скорее всего возьму svelte.

Хоть я и всецело разделяю боль от Реакта и barrel files, нести ООП в него эту боль только ухудшит.

Если в вашем первом примере вместо this.render() вызвать this.forceUpdate(), то оно будет работать так как вы ожидали

С каким бы удовольствием я бы перетащил свою команду на vue, но в компании реакт, к которому хотят и уже сделали общую библиотеку компонентов, поэтому остаётся только негодовать и вспоминать, почему то здесь, то там надо костылить

Надо поделить фронт на отдельные микрофронты, если еще не поделено. И пкрепиливать страницу за страницей, удаляя крмпоненты - 100% они вызывают головную боль. Для отального есть tailwind.

Тейлвинд не нужен ни для чего. Да вы и сами говорите: «фреймворки пытаются переизобрести апи браузера», «не нравятся разработчики, которые не могут это апи использовать или не хотят в css»

Можно и на голом css. Но tailwind предоставляет норм компоненты и ютилити классы. Это скорее как фреймворк для стилизации. Все им пользуются и не надо разрабатывать свой уникальный который не будет работать больше ни в какой компании. Есть свои плюсы.

Можно и свмому написать. Современный css крут. Но зачем?

Он предоставляет не компоненты, а полную ерунду. И свой «уникальный» атомарный фреймворк вместо него соответственно тоже разрабатывать не нужно, потому что получится аналогичное поделие.

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

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

Когнитивная нагрузка с ним гораздо меньше.

Не знаю как измерить чушь.

Не вижу дядь.

Какая то поделка очередная, которая не может в стили сама и требует тонну js. Тут же переизобретает браузер.

Знания не переносимые. Ни за что бы в проект не взял.

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

Если у вас дизайнер занимается вёрсткой, то либо вы делаете одноразовые сайты, либо неправильно распределяете работу. Сложные веб-компоненты дизайнер не сверстает правильно, потому что там нужно учитывать скриптовое поведение компонентов.

Мы правильно распределяем работу. Если тот кто придумал ux не в состоянии его сверстать, то пусть делает проще или доучивается.

Все скриптовое поведение изначально должно быть представлено дизайнером. Оно не берется из вакуума.

UX designer в 25 году обязан уметь делать верстку. Остальные - фигма-бои не достойны этой должности. Пусть двльше презентации клепают.

Откуда у вас это желание обесценить других?

Много бизнеса перешло на ваш фреймворк? Если среди него средний/крупный. Что с зарубежом?

Много денег вы заработали на Реакт? Есть ли среди ваших проектов такие, за код которых не стыдно? Готовы его продемонстировать?

Да, да, ну а интеллектуальная собственность в договоре это для вас шутка?) Так что по моим вопросам

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

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

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

Стили в первую очкредь не должны быть привязаны к js. Вообще js вторичен в браузере.

По этому мы пишем клевый переносимый код. А не исповедуем TS. Следим за когнитивной нагрузкой и избегаем случайной сложности.

Я не призывал писать стили в скриптах. Вполне достаточно писать стили в CSS.

Следим за когнитивной нагрузкой

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

Вы суинули доку на фреймворк который диктует как должен писаться css в т.ч. с валидацией стилей в TS, разве не так? И вы продвигаете этот фреймворк, т.к. ваши знакомые дяди так делают. Т.е. вы именно это и имели ввиду.

Никакого лицемерия. Побил на составные части переносом строки и все понятно. И главное переносимо между командами, проектами, компаниями.

Я не скидывал никаких ссылок и никакой фреймворк не продвигаю. Вы ошиблись адресом.

UFO landed and left these words here

Windows XP вышла в 2001, React в 2013. Первые версии React были на классах, может им и над классами нужно было сделать какую-то кросс-браузерную обертку?

React вышел во времена ie11, и в нем с событиями никаких проблем не было

Это справедливо и для событий тоже.

Аргументы так себе. Кошки скрибуться от такого объема временно создаваемых объектов, но на фоне медленного рендера это микрокопейки. Даже "пустой" компонент рисует медленно. Вот уже много лет жду когда memo не будет блокировать всю ветку компонента, а только сам компонент.

Ругаем аргументы оппонента:

Аргументы так себе.

И приводим свои:

Кошки скрибуться от такого объема временно создаваемых объектов

Ваши аргументы пря Чак Норрис в мире аргументов!

Так и говорю что рендер занимает куда больше чем вы будите экономить на создании объектов. ну вот потратили на объекты 1ms, оптимизировали ниже нуля. Только ради чего усилия если рендер занимает 100мс. Да и для 1мс это нужно создать под сотню тыс обьектов

"Кошки скрибуться" - после кодинга где нужно экономить каждый чих, react кажется кошмаром перфекциониста.

Все правильно. Реакт с самого начала начал привлекать плохие решения. Я его забросил как только посмотрел на Редакс в бою. И еще раз подтвердил свои сомнения когда посмотрел на рутер.

Мне вообще не нравится что фреймворки пытаются переизобрести апи браузера.

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

Ну всё правильно, React - это просто мусорный код, который нагружает процессор и занимает многие килобайты, мегабайты, а то и гигабайты ОП. Именно из-за React и прочих фреймворков я был вынужден заменить планшет с 2 Гб ОП 5-летней давности (он был в хорошем рабочем состоянии) на новый с 8 Гб - пользоваться многими сайтами стало просто невозможно, порой даже загрузка 1 картинку на сайт (современные со смартфонов имеют размер не менее 1 Мб) превращалась в целую эпопею. Из-за этих фреймворков чувствуется, как тупит компьютер или планшет при открытии таких сайтов. Уж лучше ванильный JS там, где он действительно нужен. К тому же рендеринг контента в браузере ухудшает SEO.

Рендер

Ну почему?! О чём они думали? Почему вызов метода render не приводит к рендеру?

Потому что если бы они сделали возможность вручную управлять рендером, это спровоцировало начинающих разработчиков плодить антипаттерны:
По типу написания

this.users.push({})
this.render()

На этом моменте сразу ломается множество преимуещств заложенных реактом.
Итого зачем так делать:

  1. Предотвращение обхода архитектуры - разработчики не могут обойти setState и мутировать state

  2. Батчинг и оптимизации - React может группировать обновления

  3. Гарантия lifecycle - все хуки вызываются в правильном порядке

  4. Передает отвественность за рендиренг реакту, который под капотом может сам решить когда придет оптимальный момент для рендеринга. Например он может выделить интервалы времени отведенные специально для рендеринга и для остальных операций.

Теперь про subscribe unsubscribe

При изменениях в нашем «сторе» мы вызываем переданный при подписке subscriber, и это вызывает ре-рендер. При анмаунте React вызывает метод unsubscribe. Поверили? А зря!

Не поверили, прижился современные паттерн "subscribe возвращает unsubscribe" который удобнее и множество библиотек пошли по его пути. Зачем хранить промежуточную ссылку если можно сразу отписаться:

// addEventListener появился в ~2000 году 
element.addEventListener('click', handler);
element.removeEventListener('click', handler);

VS

// RxJS появился в ~2010 (из Reactive Extensions)
const sub = observable.subscribe(handler);
sub.unsubscribe();

Примеры

// Не работает - разные ссылки на функции!
element.addEventListener('click', () => console.log('click'));
element.removeEventListener('click', () => console.log('click'));

// ✅ Нужно сохранять ссылку
const handler = () => console.log('click');
element.addEventListener('click', handler);
element.removeEventListener('click', handler);

VS современный паттерн:

// ✅ Не нужно сохранять handler отдельно
const unsubscribe = subscribe(() => console.log('click'));
unsubscribe(); // Просто вызываем

Пример с геттером (якобы баг react compiler)
Вы сами же включаете механизм скрытой оптимизации, нарушаете его принципы используя сайдэффекты и сами же удивляетесь почему оно не работает как ожидается. НЕ нужно ссылаться на внешние данные в рендере надеясь что они каким-то чудесным образом будут работать. Используйте React way и не будет проблем.

А если все же очень хочется зачем-то заставить реакт дергать ваш геттер (не могу представить технически обоснованной ситуации это делать), то вот вам рабочий вариант:
https://stackblitz.com/edit/vitejs-vite-ujxr5drf?file=src%2FApp.tsx

"реакт ломает JS" - некорректное заявление в данном контексте.

Про оптимизации

Теперь, каждый раз при нажатии на кнопку будет происходить следующее:
React вызовет функцию Component
Функция Component вызовет useState, что приведет к созданию:
Двух массивов

useCallback, useMemo нужно рассматривать в первую очередь для работы в связке с HOC memo, что бы гибко обходить самую распространенную причину лагов - избыточный перерендер, а не для того что бы экономить память. Из моего опыта примерно 80% причин лагающих интерфейсов были связанны именно из-за лишнего ререндера и благоряда useMemo + мемоизированным компонентам, эта проблема легко решалась.

эта проблема легко решалась

Это, разумеется ложь. Тут я подробно это разбираю:

это спровоцировало начинающих разработчиков плодить антипаттерны:По типу написания

this.users.push({})

this.render()

А когда вдруг this.users.push(user) стал антипаттерном? А, точно, когда те же ребята изобрели редакс, и продали всем идею, что this.users.push({}) неправильно, а правильно что-то вроде:

const usersSlice = createSlice({
  name: 'users',
  initialState: {
    users: []
  },
  reducers: {
    addUser: (state, action) => {
      return {
        ...state,
        users: [...state.users, action.payload]
      };
    }
  }
});

На этом моменте сразу ломается множество преимуещств заложенных реактом

Какая-то слабая архитектура, раз ее ломает обычный JavaScript код.

  1. Предотвращение обхода архитектуры - разработчики не могут обойти setState и мутировать state

  1. Батчинг и оптимизации - React может группировать обновления

Дороговато он за это берет.

  1. Гарантия lifecycle - все хуки вызываются в правильном порядке

В классовом компоненте? Там запрещены хуки, а вызов метода render никак не мешает.

может сам решить когда придет оптимальный момент для рендеринга

Тогда, когда пользователю нужно увидеть новые данные.

рижился современные паттерн "subscribe возвращает unsubscribe" который удобнее и множество библиотек пошли по его пути

И? В чем именно проблема?

class Store {
  subscribe() {
    return this.unsubscribe;
  }

  unsubscribe() {}
}

Или вы меня пытаетесь убедить, что useSyncExternalStore(subscribe, getSnaphot, getServerSnapshot) удобнее чем useSyncExternalStore(store)? Не убедили.

А если все же очень хочется зачем-то заставить реакт дергать ваш геттер

Что? Я должен заставлять React не ломать JavaScript?

не могу представить технически обоснованной ситуации это делать

Без комментариев.

то вот вам рабочий вариант:https://stackblitz.com/edit/vitejs-vite-ujxr5drf?file=src%2FApp.tsx

за очередной костыль
за очередной костыль

useCallback, useMemo нужно рассматривать в первую очередь для работы в связке с HOC memo, что бы гибко обходить самую распространенную причину лагов - избыточный перерендер, а не для того что бы экономить память

То есть, нам нужно использовать костыли useCallback и useMemo, чтобы работал еще один костыль – HOC memo, чтобы обходить ре-ренддеры возникающие из-за неравенства ссылок текущих и предыдущих пропсов возникающих вообще из-за использования хуков? Третий раз спасибо.

Sign up to leave a comment.

Articles