Да, я его действительно ненавижу. Мне кажется, что команда React'а презирает разработчиков, и я презираю их в ответ. Все их решения направлены на то, чтобы сделать разработку сложнее, медленнее и непредсказуемее. На сегодняшний день они даже умудрились сломать работу JavaScript. Уму непостижимо, почему им это сходит с рук.
Рендер – это не рендер
Я знаком с React'ом с первой его версии. Тогда я фрилансил, и моим любимым стеком был ModX с его шаблонами + jQuery. Чуть раньше React'а появился шаблонизатор Fenom, и он был очень крутой. Увидев JSX, я сначала подумал: «Круто! Это же как Fenom, только в браузере». Но первое разочарование наступило сразу же. Рендер — это не рендер:
class MyComponent extends React.Component {
count = 0;
constructor() {
super();
setInterval(() => {
++this.count;
this.render(); // Этот вызов ни к чему не приведёт!
});
}
render() {
return <div>Current count: {this.count}</div>;
}
}
Ну почему?! О чём они думали? Почему вызов метода render не приводит к рендеру? Это же так очевидно! Просто представьте параллельную вселенную, где этот класс был бы реализован вот так:
class MyComponent extends Component {
// Вызывается, когда сверху пришли новые пропсы;
// компонент сам решает, нужно ли вызвать рендер.
onPropsChange() {}
// Вызывается перед удалением ноды из DOM.
onDisconnected() {}
// Вызывается после вставки ноды в DOM.
onConnected() {}
// Реально рендерит (обновляет DOM).
render() {}
}
Скольких проблем попросту бы не существовало? С какой легкостью мы бы подключали внешние источники данных! Например:
// import { makeObservable, autorun } from 'kr-observable';
// Или
// import { makeObservable, autorun } from 'mobx'
// Или
import {
reactive as makeObservable,
watchEffect as autorun
} from 'vue';
const state = makeObservable({})
class MyComponent extends React.Component {
constructor() {
super();
this.disposer = autorun(this.render);
}
onDisconnected() {
this.disposer();
}
// Реально рендерит (обновляет DOM).
render() {
if (state.loading) return <div>Loading...</div>
return <div>{state.data}</div>
}
}
Но это не путь React'а. В Solid.js, например, мы можем это сделать одной строчкой кода: enableExternalSource, а React всячески препятствует любой интеграции. React даже контрибьютингу всячески препятствует!
Фиктивный open-source
Да, React open-source, но контрибьютеров не ждёт. Недавно мне понадобилось посмотреть внутренности хука useSyncExternalStore (о котором ниже), но мне это не удалось, потому что репозиторий React'а — это лабиринт, и для навигации там нужен путеводитель. Смотрите сами.
Идём в репозиторий React'а, в пакет use-sync-external-store и видим это:
import * as React from 'react';
export const useSyncExternalStore = React.useSyncExternalStore;
Это всё. Зачем целая директория для этого пакета?
Продолжаем искать. Это хук, значит, искать надо среди хуков, верно? Идём в пакет react, в файл ReactHooks.js и видим это:
export function useSyncExternalStore<T>(
subscribe: (() => void) => () => void,
getSnapshot: () => T,
getServerSnapshot?: () => T,
): T {
const dispatcher = resolveDispatcher();
return dispatcher.useSyncExternalStore(
subscribe,
getSnapshot,
getServerSnapshot,
);
}
И тут ничего. Может, resolveDispatcher поможет? Пробуем:
import ReactSharedInternals from 'shared/ReactSharedInternals';
function resolveDispatcher() {
const dispatcher = ReactSharedInternals.H;
return ((dispatcher: any): Dispatcher);
}
Опять нет! Но нам дали новую подсказку: shared/ReactSharedInternals. Давайте посмотрим:
import * as React from 'react';
const ReactSharedInternals =
React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
export default ReactSharedInternals;
Потрясающе. Всё для людей. Образцовый open-source репозиторий!
Какова вероятность, что, обнаружив какой-то баг, я смогу попробовать его исправить и отправить PR, а не ждать и надеяться, что мейнтейнеры когда-нибудь сделают это сами? Около нулевая. Именно поэтому в репозитории React'а висят баги от 2014 года — не потому, что их кто-то другой не может исправить, а потому что это фиктивный open-source. Это системная непрозрачность, и она прослеживается во всём, что делает React.
Хуки – великое разочарование
Начнём с упомянутого выше useSyncExternalStore. Казалось бы, React одумался, сдался, признал, что внешний стейт — это не грех, а реальность, и предоставил разработчикам удобный инструмент для интеграции внешнего стейта с прекрасным, лаконичным и очевидным API. Реализуем интерфейс ExternalStore:
class Store implements ExternalStore {
subscribe(subscriber) {
this.subscribers.add(subscriber);
return this.unsubscribe;
}
unsubscribe() {}
}
// и используем
const store = new Store();
function Component() {
useSyncExternalStore(store)
// ...
}
Красиво, же?
При изменениях в нашем «сторе» мы вызываем переданный при подписке subscriber, и это вызывает ре-рендер. При анмаунте React вызывает метод unsubscribe. Поверили? А зря! Команда React'а ставит собственную «религию» выше удобства разработчиков, поэтому реальный интерфейс useSyncExternalStore максимально неудобный и неэффективный. Вместо одного объекта, имплементирующего некий интерфейс, нам нужно передать в этот хук три функции:
function Component() {
useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
// ...
}
Как? Как можно было принять такое решение? О чём они думали? Это же абсурд! Я понимаю, что разработчики React'а ненавидят ООП, но не до такой же степени?
Из-за такого API мы вынуждены изменить класс Store. Мы больше не можем переиспользовать объявленные в прототипе методы subscribe и unsubscribe, а должны создавать их для каждого экземпляра отдельно:
class Store implements ExternalStore {
// новые стрелочные функции для каждого экземпляра
// чтобы не потерять this
subscribe = (subscriber) => {}
unsubscribe = () => {}
}
Потребление памяти выросло, создание экземпляров — медленнее из-за лишних аллокаций, API — неудобнее. И ради чего?
Возможно, вы впервые слышите об этом хуке, но ведь остальные не лучше; я бы даже сказал — ещё хуже. Вот, например, useState:
function Component() {
const [state, setState] = useState({ value: 0 });
const update = () => setState(prev => ({ value: prev.value + 1 }))
return (
<button onClick={update}>
{state.value}
</button>
)
}
Теперь, каждый раз при нажатии на кнопку будет происходить следующее:
React вызовет функцию
ComponentФункция
ComponentвызоветuseState, что приведет к созданию:
Двух массивов
Один создает и возвращаетuseState, положив в негоstateиsetState, второй создаем мы, чтобы деструктуризацией достать эти значения;
Одного объекта
Объект{ value: 0 }, который передается параметром вuseState, создается при каждом рендере. И хотя он не будет использован, и сборщик мусора его очистит мы все равно потратили время на его создание + какое-то время он будет болтаться в памятиСоздается новая функция
updateи новая функция, которую мы передает колбэком вsetStateи дополнительно – еще один объект{ value: prev.value + 1 }
Это же безумие! Это издевательство и над сборщиком мусора и над JIT компилятором!
React знает о проблеме — поэтому предлагает костыли типа useCallback или useMemo. Но это не решение, а признание провала.
Я проводил много собеседований и не вспомню случая, когда кандидат на позицию React-разработчика не утверждал бы, что useCallback избавляет от лишних аллокаций. Но ведь это не так. Хук useCallback запоминает аргумент при первом рендере, а все последующие просто создают ненужные объекты типа Function, которые сразу же уничтожаются сборщиком мусора за ненадобностью. При этом документация React'а нагло врёт:
useCallback is a React Hook that lets you cache a function definition between re-renders.
Cache a function definition? Что? Каким это магическим образом вы кешируете объявление функции? Вы переизобрели JavaScript? Это же чушь!
const cache = new Set;
function pseudoUseCallback(calback, deps) {
cache
.add(calback)
.add(deps)
}
for (let i = 0; i < 1000; i++) {
pseudoUseCallback(() => {}, [])
}
Кажется, что даже джун не будет сомневаться в том, что после выполнения этого кода в кеше окажется 1000 анонимных функций и 1000 пустых массивов. Почему же React пытается нас убедить в обратном, если useCallback точно так же работает?
Я отдельно ненавижу хуки, и у меня нет желания уделять им много внимания. Я лишь хотел показать, что, кроме других проблем, с которыми, я уверен, вы не раз сталкивались, они ещё и жутко неэффективны. Отдельное непонимание у меня вызывают разработчики, утверждающие, что хуки удобны. Серьёзно? Вот это удобно?
? Do not call Hooks inside conditions or loops.
? Do not call Hooks after a conditional return statement.
? Do not call Hooks in event handlers.
? Do not call Hooks in class components.
? Do not call Hooks inside functions passed to
useMemo,useReducer, oruseEffect.? Do not call Hooks inside
try/catch/finallyblocks.
И это удобно?
const [state, setState] = useState({ value: 0 });
const update = () => setState(prev => ({ value: prev.value + 1 }))
Может, и удобно, но React'у, а не разработчику. Кстати, об этом...
Мы в заложниках искусственных ограничений
React уделяет больше внимания не тому, чтобы вам было удобно, а тому, чтобы вам было больно с него слезть. Серьёзно, смотрите сами:
function Component() {
return (
<div className="bar">
<label htmlFor="username">Label</label>
<input />
</div>
)
}
Опытный React-разработчик даже не заметит подвоха, а неопытный задастся вопросом: это что за атрибуты такие, className и htmlFor? А это, счастливый не-React-разработчик, для того, чтобы тебе было сложнее портировать код на что-то другое!
Звучит как нелепая теория заговора? Да! Но моё объяснение не более нелепое, чем официальное из документации React, например:
Since
foris a reserved word in JavaScript, React elements usehtmlForinstead.
Правдоподобно, не правда ли? Значит, class тоже зарезервирован? Ну, как вам сказать... Давайте лучше покажу:
class FooElement extends HTMLElement {
connectedCallback() {
this.root = this.attachShadow({ mode: "open" });
this.root.innerHTML = `
<div>
For value: ${this.getAttribute("for")}<br/>
Class value: ${this.getAttribute("class")}
<div>
`;
}
}
window.customElements.define("foo-tag", FooElement);
export default function App() {
return (
<foo-tag
class="some-class-name"
for="some-value"
>
</foo-tag>
);
}
Здесь мы создали кастомный HTML-элемент и отрендерили его React'ом, передав два атрибута — class и for. Убедиться, что всё работает, можно в песочнице или в следующем параграфе документации React'а:
If you use React with Web Components (which is uncommon), use the class attribute instead.
Кстати, о веб-компонентах — их более-менее внятная поддержка в React'е появилась только в 19-й версии. Лет семь сопротивлялись! Всё из-за того, что они реализованы на классах?
Зачем мы тогда пишем tabIndex, className, htmlFor и т.д.? А event.currentTarget?
Нормализация событий – решение выдуманной проблемы
Почему мы вынуждены писать event.currentTarget? Ах да, потому что:
SyntheticEvent… это кросс-браузерная обёртка над нативными событиями.
Но зачем нужна обёртка, если у событий единый стандарт? React застрял в 2010-х, хотя вышел в 2013. Его «нормализация» событий — это попытка решить проблемы IE8 и старых Firefox, которые давно умерли? Нет ни одной причины иметь SyntheticEvent, кроме как сделать код для React'а несовместимым с чем-то другим.
Compiler – апогей абсурда
Да. Compiler прекрасно отражает степень оторванности React'а от реальности. Тут комментировать — только портить. Лучше посмотрите:
const MyOwnObject = {
get uniqueId() {
return Math.random()
}
}
function App() {
const [count, setCount] = useState<string | number>(0)
const up = () => setCount(Math.random())
return (
<div>
<div>Current count {count}</div>
<div>Current unique key {MyOwnObject.uniqueId}</div>
<button onClick={up}>Update</button>
</div>
)
}
Что мы ожидаем увидеть при ре-рендере компонента в качестве uniqueKey? Очевидно, какое-то уникальное число. Но нет! Compiler намертво закеширует значение, полученное при первом обращении к MyOwnObject.uniqueId, и никогда больше к нему не обратится!
У меня просто нет слов, чтобы это комментировать. Страшно представить, сколько вещей может сломать этот компилятор, если он умудрился сломать даже JavaScript!
Представьте, что Date.now() после первого вызова стал бы возвращать одно и то же значение для последующих. Это был бы баг в движке. Это бы сломало интернет. Совсем! А в React — это «фича компилятора». Апогей абсурда! У разработчиков React'а какая-то своя альтернативная реальность?
React больше не про разработчиков. Он про контроль.
Контроль над тем, как вы пишете код, как вы думаете, как вы понимаете JavaScript.
И пока мы молча принимаем его правила — он будет становиться всё более оторванным от реальности.
Поэтому я ненавижу React.
Комментарии (33)

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

dan_sw
23.10.2025 08:57Рендер — это не рендер
Чего? Вы сейчас серьёзно? Рендер не рендер только потому, что Вы его явным образом вызвать не можете? Он вызывается периодически при каких-либо изменениях 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. При правильной работе с ним можно добиться очень неплохого результата.

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

nihil-pro Автор
23.10.2025 08:57Он вызывается периодически при каких-либо изменениях states/props в компонентах (ну и при перерисовке родительских компонентов и других, в принципе разумных ситуациях).
Написали бы сразу – вызывается непредсказуемо.
но то, что функцию рендера нельзя явным образом вызвать - не проблема, а механизм работы самого React.js
Потрясающе предсказуемо. Тошнит уже от этих «механизмов» реакта.
Вы бы хотели вызывать функцию рендера?
Да. У меня есть какой-то JSX, в нем обращение к каким-то свойствам, какие-то условные конструкции – не важно. Просто пусть метод render отрендерит результат этого JSX выражения. В чем проблема?
exportdefaultconnect( mapStateToProps, mapDispatchToProps)(MyComponent);Уже даже сам автор этого недоразумения отказался от своего детища, а вы продолжаете.
И ничего плохого в этом нет. Работает, даже на продакшене.
Это сильно, конечно!
А если серьёзно, какая разница какой у них код-стайл?
Почти со всем согласен. Это печальная ситуация.
Вы же себе прямо противоречите.
если говорить о хуках, то это вообще в целом функциональщина JS. Тут нет ООП, тут есть функциональное программирование на JS.
Я описал в статье сколько это на самом деле стоит.
Я думаю, что это было необходимо из-за особенностей механизмов самого React.js. Да и обязан ли он быть кому-то "библиотекой, с которой можно перенести всё на Solid.js"?
Он обязан не ломать браузерное API.
Если бы он этого не делал (не кэшировал), то различные конфигурационные константы приложения (находящиеся не в переменных окружения) постоянно заново бы пересоздавались.
Что? Это как? Как вызов функции может привести к пересозданию какого-то внешнего конфигурационного объекта? Вы действительно считаете, что сломать базовое поведение JavaScript где обращение к геттеру приводит к его вычислению – это нормально?
Постоянные пересоздания объектов и так мешают React.js быть более эффективным в контексте аллокаций памяти.
Так может вы не будете защищать хуки? Это ведь они к этому приводят, и им приходится чинить их еще больше ломая все?
как-то всё надумано и по верхам
По верхам? Ну ладно.

Vlad_IT
23.10.2025 08:57Я описал в статье сколько это на самом деле стоит.
Справедливости ради, созданные функции даже не успеют попасть в old space, как их грохнет GC. А объекты созданные на каждый рендер тоже легкие, т.к. у них стабилизированный shape.
У реакта куда больше проблем с перфом, чем вами описанные
1) Ужасно долгий renderToString на SSR, который не даст превысить 40-50 RPS на среднем по производительности ядре серверов.
2) VDOM дерево куда сильнее бьет по GC, т.к. там не всегда стабильная структура и очень много лишних созданий shape'ов.
3) Громадный бандл, это дорого по компиляции/парсингу.
На этом фоне создание неиспользованных функций в рендере это вообще спички, особенно учитывая современные движки, которые такие вещи круто оптимизируют.Поэтому, когда я хочу писать быстрые приложения, но оставаться в экосистеме реакта (по крайней мере, чтобы найм не усложнить) я использую Preact+Signals. Сильно проще выполнение diff, т.к. глубина дерева сигналов меньше чем vdom, сильно меньше бандл, плюс базовая скорости выше, чем в реакте.
Так может вы не будете защищать хуки? Это ведь они к этому приводят, и им приходится чинить их еще больше ломая все?
Тут сильно вкусовщиной пахнет. Я вот не люблю классы в js, т.к. на старых движках там все плохо с ними оптимизировано, от них громоздкий бандл получается и плюс всякие tensor не умеют укорачивать имена приватных свойств у класса, любят убивать дефолтные свойства у классов, оставляя их только в конструкторе и тем самым ломая тот же shape объекта, особенно если в разных вызовах конструкторов может быть инициализирован разный набор свойств.
То есть если вы заботитесь о производительности и памяти, классы не ваш выбор, нужно исследовать в конкретном случае, лучше/также ли они по перфу и памяти по сравнению с функциями, создающими объекты.

dan_sw
23.10.2025 08:57Написали бы сразу – вызывается непредсказуемо.
Ну, я же не буду врать :)
Тошнит уже от этих «механизмов» реакта.
Ну так используйте другую библиотеку/фреймворк.
Просто пусть метод 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 поведение не по документации! Компилятор плохой, потому что геттер не вычисляется!". Это крайне не профессиональный подход.
По верхам? Ну ладно.
Ну да.

nihil-pro Автор
23.10.2025 08:57А вообще... я буквально только что решил проверить ваш этот пример с геттером и вот что обнаружил - он нормально работает в 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) Это песочница. Вы можете посмотреть код и понажимать на кнопочку, чтобы убедится, что геттер не работает:

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

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

dan_sw
23.10.2025 08:57Я проверил очень тщательно, а вы нет.
Какой вы молодец :) Тогда без явно включенного компилятора тоже имело смысл бы проверить и добавить в статью, что вот без него - всё вполне работает. Мб тут есть баг, и есть смысл о нём команде React.js сообщить.
Вы же предоставили ссылку на пустую песочницу где ничего нет
Я предоставил ссылку на веб-приложение, в котором можно протестировать тот код, который я приложил после ссылки. Мне было лень шарить конкретный пример, к тому же код можно скопировать и вставить куда угодно, а вот ссылка может быть битая (я переходил по вашей ссылке из статьи, и мне не удалось посмотреть ваш пример. Кстати, до сих пор).
используется React с включенным компайлером
Справедливо. Его действительно нужно явным образом включать. Тут я ошибся. Вы же не сам React.js за это "минусовали", а только лишь за компилятор с babel-plugin-react-compiler и vite (исходя из сборки).
Впрочем, уверен, что этой ошибки можно избежать, если покапаться в настройках используемого компайлера (мб ещё плагины на это влияют, подключаемые или vite выбранный в качестве сборщика).
В любом случае странно, что без явного определения компилятора всё работает корректно (ссылку на редактор и код для проверки я оставил в комментарии выше), а с включённым компилятором - нет. Вероятно тут проблема комплексная.... где-то явно есть баг, который не полечили.
Остальное комментировать неинтересно.
Ну... эм... никто и не просил, собственно :))

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

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

DmitryKazakov8
23.10.2025 08:57Хорошо структурированный код - это не только разбиение по семантическим папкам (пакетам) и отдельным файлам со связанной логикой и заботой о переиспользовании. Это еще и удобный DX в виде быстрых переходов на место объявления. Здесь же чтобы попасть в нужное место приходится делать десяток быстрых переходов, причем иногда они динамические и могут быть не связаны типами, и приходится делать "поиск по всем репозиториям". Другими словами - "обертка на обертке с названиями переменных в 1 символ".
Также подобный подход очень усложняет дебаг. Если вы видели стектрейсы реактовых ошибок со стеком вызовов в 50 функций, то наверняка отказались от идеи по ним выяснить проблему. В правильно структурированных библиотеках же стектрейс может быть очень полезен и поможет быстро найти проблему.
Конечно, 5000 строк лапши - это никуда не годится, но и с реактовым подходом тоже не все гладко, и можно было бы сделать лучше.

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

UbuRus
23.10.2025 08:57Автор не знает что существует поиск, я нашел сорцы useSyncExternalStore за минуту прямо не выходя из браузера: https://github.com/search?q=repo%3Afacebook%2Freact useSyncExternalStore&type=code
Еще автор не знает про то, что реакт это не только замена jquery, но очень хитрый рантайм, у которого нет аналогов https://overreacted.io/progressive-json/

nihil-pro Автор
23.10.2025 08:57Автор не знает что существует поиск, я нашел сорцы useSyncExternalStore за минуту
Приведите код, пожалуйста, а не ссылку на результаты поиска.
но очень хитрый рантайм, у которого нет аналогов
Смешно.

UbuRus
23.10.2025 08:57Приведите код, пожалуйста, а не ссылку на результаты поиска.
Третья ссылка в результах код клиента 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

nihil-pro Автор
23.10.2025 08:57Третья ссылка в результах код клиента
Нет. Это не реализация
useSyncExternalStore, а «обманка» которую можно использовать вместо реальногоuseSyncExternalStoreв реакте ниже 18-ой версии.Четвертая код сервера
Ничего общего с реализацией это не имеет, код бы хоть посмотрели.

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

UbuRus
23.10.2025 08:57Какова вероятность, что, обнаружив какой-то баг, я смогу попробовать его исправить и отправить 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

isumix
23.10.2025 08:57В Реакте объединили управление ВИД-ом и СОСТОЯНИЕ-м в одиом флаконе. И от этого все мучения - хуки=хаки и ре-рендеринг. Которые команда Реакт пытается преодолеть все эти годы: batching, memoization, concurrency, SSR, RSC, compiler...
Если разделить управление видом и состоянием на две разные сущности/ответственности, то код становится проще в разы, и что самое интересное клиентский код тоже.
Например взять Фьюзор - который только управляет DOM-ом и вместе с которым можно использовать любую библиотеку стейт-менеджмента, или даже использовать обычные переменные там где этого достаточно.
const ClickCounter = ({count = 0}) => ( <button click_e_update={() => count++}>Clicked {() => count} times</button> );
jakobz
23.10.2025 08:57Если так хочется разделить - делаешь хук к компоненту в отдельном файле, делов то.
К реакту можно много где домотаться, но статья - вообще мимо. Человек хочет ангуляр просто.

nihil-pro Автор
23.10.2025 08:57но статья - вообще мимо
Я описал по пунктам все свои претензии, привел аргументы, ссылки, примеры кода и даже в некоторых случаях предложил альтернативу. Какие у вас контр-аргументы кроме «вообще мимо»?
Человек хочет ангуляр просто
А вы всегда придумываете позицию за оппонента, причем максимально нелепую? Статья про недостатки конкретного инструмента, а не про сравнение разных инструментов, и если вы выдаете свои домыслы за мою позицию, то может их стоит хотя-бы чем-то подкрепить?
На данный момент, мой выбор это 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.

nin-jin
23.10.2025 08:57Preac + Signals - 12KB
$mol_jsx_view - 6KB
При этом последняя идёт в комплекте с более совершенной системой реактивности.
nihil-pro Автор
23.10.2025 08:57Так то да, но с преактом я на рынке быстро найду реакт разработчика, посажу его клепать компонентики, и он даже не поймет, что пишет не на реакте. С молом будет сложнее.

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

nihil-pro Автор
23.10.2025 08:57Не спорю, все так. Для себя я разработчиков делю, условно, на две категории – инженеры и ремесленники. Инженера найти трудно, ремесленника нет. Реакт разработчик для меня скорее ремесленник.

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

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

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

Serge711
23.10.2025 08:57Если в вашем первом примере вместо this.render() вызвать this.forceUpdate(), то оно будет работать так как вы ожидали
Bertram11
Всё меняется. Правила игры меняются. Это бизнес. Как там фраза знаменитая звучит: «Меняйся или сдохни» — фраза из фильма «Человек, который изменил всё» (Moneyball, 2011)
Bertram11
Ох мой комментарий не понравился) извините