Главный миф о Дэне Абрамове — что он «создал React». Но хотя это и не так, сейчас он имеет самое прямое отношение к фреймворку, так что поговорить с ним про React очень интересно. Обычно Дэна не увидеть на российских конференциях, но нам помог онлайн-формат, и на HolyJS его подробно расспросили Наталия Теплухина (член core team Vue.js) и Наталия Короткова (занимается веб-проектами с 2010 года).


Тут получился не поверхностный разговор о погоде, а обсуждение технической конкретики для тех, кто вдается в детали. Но под конец были и более общие вопросы: когда появится новая документация React, переписанная с нуля? Что не так с Redux? Как Дэн воспринимает холивары о фреймворках? Что он может рассказать о релокации?


Так что, если не осилите все технические детали в первой половине интервью, переходите ко второй, там попроще. Под катом — и видеозапись разговора, и текстовая расшифровка.



Оглавление



О Suspense


— Первый вопрос немного философский. Начну его с вводной. Чем стабильность отличается от стагнации? В 17-м апдейте React у нас не было никаких классных фич и заметных обновлений, хотя ожидались какие-то глобальные апдейты. Почему React вообще сменил версию с 16 на 17, ради чего это было?


Изначально не планировали такой 17-й версии, которая получилась. Действительно, планировали апдейты, связанные с конкурентным режимом, Suspense. Но так сложилось, что было несколько проблем, которые мы хотели пофиксить довольно давно, с 2014 или 2015 года. Они связаны с ивентами. И мы снова столкнулись с ними внутри.


Была конкретная проблема. Вы, наверное, помните редактор Atom: там с ней столкнулись до того, как редактор «закончился». Они какое-то время использовали React, на нем можно было писать плагины, и столкнулись с тем, что если вложить в одно React-дерево другое — они плохо работают вместе.


Если у тебя File Explorer на React 13, а Project View на 14-м, конкретная вещь, которая ломается —
event.stopPropagation(). Эти реакты не знают друг о друге, не могут координировать события.


И мы столкнулись с похожей проблемой: переписывали сайт (Facebook — прим. ред.) и хотели перевести некоторые части на новую версию, которая убирает совсем старые недокументированные фичи из 2014–2015 годов.


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


Мы решили пофиксить события, работал в основном Доминик (Dominic Gannaway — прим. ред.). Большое изменение — события перенеслись. Раньше они подписывались на документе, а теперь делегируются к корню дерева React. Даже если вы используете две версии React, что в целом не очень хорошо, в большом приложении есть разумный компромисс: какие-то старые трехуровневые диалоги можно держать на старой версии, а основное приложение перевести на новую. Благодаря этому изменению они могут сосуществовать, код можно переводить с одной версии на другую, и это не должно сломать приложение в целом. Может, придется немного подтянуть его.


Мы не можем взять какой-то апдейт и сделать тяп-ляп — нужна стратегия, которая работает для огромных кодовых баз с сотнями тысяч компонентов. Поэтому мы говорим, что нет новых фич, но начиная с React 17 как раз будет большая фича — в больших кодовых базах появится возможность вкладывать кусок приложения одной версии в кусок приложения другой версии.


Таким образом в 18-й версии это позволит нам убрать какие-то вещи, которые мы не могли убрать раньше, так как от них, к сожалению, до сих пор зависит много кода.


Поэтому мы характеризуем 17 версию словами «stepping stone» (нечто вроде «ступень на пути, позволяющая перейти к большему» — прим. ред.).


— Почему вы так долго пилите Suspense, что мешает его релизнуть?


Давайте я сначала расскажу, как мы в принципе работаем, а потом отвечу на вопрос.


Мы не просто команда разработки, мы команда Research & Development. В продуктовой команде бывает, что кто-то просто просит какую-то штуку, и её делают. У нас немного другой подход, потому что продукт другой.


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


Если начать думать, как встроить само понятие асинхронности в UI-программирование, то это довольно обширная область. И одна из основных идей в Suspense — мы хотим утащить понятие того, что какой-то компонент не готов зарендериться, что он ждет каких-то данных или чего-то ещё, в декларативную модель, как React делает с другими вещами. Вместо того, чтобы приходилось делать что-то вручную, это становится элементом дерева.


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



У нас одновременно с этим делался новый сайт, первый раз полностью на React, и мы вложили эту идею в фундамент. И много чего узнали. Наше изначальное представление о том, как это будет работать, конечно, было неправильным — мы сделали ошибки, и их нужно было исправлять.


И в целом у нас это уже хорошо работает c Relay. То есть у нас есть опенсорсная библиотека для GraphQL, которая использует Suspense. Но мы знаем, что в опенсорсе никто не использует Relay, мы хотим общее решение.


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


Мы по-прежнему развиваем эту идею. Есть конкретные вещи, которые, я надеюсь, мы сможем показать в ближайшие месяцы. Они дополняют картинку и делают её целостной. (прим. ред.: в конце декабря были представлены React Server Components)


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


О Concurrent mode


— Как вы конкретно исследуете эту проблематику? Насколько я знаю, вы тестируете Concurrent mode и Suspense data fetching на Next.js-проекте. Можешь рассказать об итогах тестирования? Помогает ли это вам в развитии React?


Да, если кто-то не знает, у нас есть активная коллаборация с Next.js, это React-фреймворк. Это коллаборация между нами, Next.js и кусочком команды Google Chrome. У нас нет какого-то направления вроде «помогать только Next.js», просто в разное время работаем с разными командами над разными проектами.


Понятно, что людям нравится React, но делать всё на клиенте — неэффективно. И если хочешь сделать прочный фундамент, на котором можно строить не беспокоясь, что ты делаешь что-то неэффективное, нужен какой-то фреймворк. Либо самописный, либо из коробки.


И в этом смысле Next.js — неидеальная, но хорошая база, на которой можно строить. И для нас, если мы хотим ввести какие-то best practices — это хорошее место, и другие все равно скопируют. И у Google такой же интерес, они до этого пытались делать по-своему, что-то у них получалось.


Те, с кем мы работаем, понимают, что люди будут использовать то, что удобно, поэтому самое эффективное, что мы можем сделать — улучшать инструменты, которые используют люди. Для их улучшения нужна какая-то база.


Мы внутри Facebook используем Concurrent mode больше года, новый сайт работает полностью на нём. Понятно, что новый сайт неидеальный (одна кодовая база и сотни людей), там тоже свои сложности: иногда люди заходят, кликают, видят баг и говорят — о, Concurrent mode. Но нет, это не совсем так работает.


Во главе — Concurrent mode, потому что очень сложно сделать сайт такого объёма на классическом React. Это не то же самое, что перевести существующее приложение — мы и с Next.js работаем, у них есть какие-то клиенты. И через них можно попробовать перевести один проект на Next.js и посмотреть, что будет на Concurrent mode.


У них есть своя data fetching-история с getInitialProps, которая в чем-то нас вдохновила. Это один из ингредиентов, который мы получили от Relay, нашего собственного ресёрча, а теперь мы хотим посмотреть — можем ли мы дополнить то, что Next.js сделал с getInitialProps, внести свои идеи туда и посмотреть более общее решение, которое имеет смысл попробовать.


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


По экспериментам — насколько знаю, Concurrent mode уже пробовали в каких-то проектах. На Next.js Conf недавно был доклад от команды Chrome, в котором они показали слайд с изменениями в хорошую сторону.



Вот этот момент в докладе.


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


— Будет ли Concurrent mode работать с React Native, и правда ли Facebook использует React Native, который отличается от того, что лежит в общем доступе на GitHub?


Мы всегда используем не совсем то, что в опенсорсе, потому что у нас иначе собираются ошибки, есть существующая инфраструктура, с которой нужно интегрироваться. Поэтому мы не используем npm package, а билдим вручную и меняем некоторые файлы.


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


Сам билд можно сделать, если заклонить репозиторий React на GitHub. Можно сделать yarn build --type=FB и получить те же бандлы, что мы используем. Только там будут внутренние референсы.


Про конкурентный режим React Native — сейчас он там не включен, и я думаю, что это один из проектов, над которым мы будем работать или помогать работать в 2021 году. Но он будет позже, чем стабильный вебовый релиз, потому что там просто больше работы.


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


Мы столкнулись с тем, что на React Native легко делать экраны приложения, но трудно создать что-то встраиваемое. Например, ленту новостей Facebook никто не будет никогда переписывать на React Native, там уже семь лет коду. И, например, хочется встроить какой-нибудь feed item на React Native, но поскольку он всегда был асинхронным, его нельзя отрендерить, измерить, чтобы он синхронно встал в ленту новостей, пока ты скроллишь.


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


Мы не «сделали шажок для веба и потом портируем на React Native». Всё задумывалось как единое целое, просто для веба это быстрее сделать.


О state managers


— Почему unstable_observedBits «анстейбл», почему аналогичную функциональность не реализуют в паблик? Это сильно упростило бы state management для маленьких и средних приложений.


Почему «анстейбл»? Потому что это очень странный API, которым сложно пользоваться. Я думаю, мы это точно уберем.


Почему люди думают, что им это нужно? Тут смотря как структурировать приложение. Если весь тип state вынести наверх, и потом где-то глубоко использовать, то может быть неэффективно, что у тебя слишком много перерендеривается и так далее. Часто это решается тем, что state нужно выносить туда, где он используется, а не пихать наверх.


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


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


И в таком случае я думаю, будет спорный вопрос, что нужен такой API для state management. Если проблему закрыть именно с тем, что слишком агрессивно всё ререндерится автоматической мемоизацией, то результат будет похож на тот, что у Svelte или Vue.


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


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


— Почему React всегда позиционировался как библиотека для View-слоя с возможностью хранить и обрабатывать данные, но никогда не предоставлял API для интеграции с внешними источниками данных, например, state managers?


И похожий вопрос: так как GraphQL — тоже технология Facebook, и, к примеру, в Redux можно увидеть, что какая-то его часть встроена в React посредством hooks, планируется ли внедрять похожую историю с GraphQL, к примеру, как Relay для стандартизации? Будут ли предусмотрены какие-то API, чтобы сделать state manager в React более стандартизированным?


У меня первая реакция на такие вопросы — я не знаю, что такое state management, не понимаю, что люди имеют в виду, когда говорят об этом. Может, у меня профдеформация, потому что в React-команде мы просто думаем об этом иначе.


В React уже есть понятие State, это UI-состояние, его менеджишь тем, что просто используешь State, если нужно передать глубоко, используешь контекст и все дела.


Есть вещи, которые туда класть неудобно. В основном — то, что не является UI-состоянием. Я не имею в виду бизнес-логику, это ещё одно слово, значения которого я не знаю, а я имею в виду кэши. Например, если ты фетчишь данные, у тебя есть, скажем, страница Twitter Feed, и эти твиты — не State.


Понятно, что обычно мы используем State, если пользуемся чистым React, или используем Redux, кладём всё туда, но так или иначе оно в конечном итоге становится реактовым State где-то внутри или копируется туда.


Но это не состояние, основанное на пользовательском вводе, а, скорее, копия чего-то, что есть на сервере. И эту копию всегда можно вытащить с сервера. Её не всегда можно редактировать локально, потому что если что-то постишь, нужно пойти на сервер — тогда уже придет какой-то результат.


Мы думаем интегрироваться с кэшами или иметь даже свой кэш, чтобы библиотеки его могли использовать и интегрироваться с React. Для данных сервера это, конечно, имеет смысл, потому что это другой тип данных. И это как раз то, над чем мы работаем с Suspense.


— Получается, что у людей возникают проблемы вроде zombie children или нестабильного батчинга, я так понимаю, в интеграции со State-менеджерами. Также неясно, как интегрироваться с конкурентным режимом. То есть для большинства state management — это не совсем тривиально.


Тут вопрос подразумевает, что вместо того чтобы пользоваться React для UI-состояния, мы пихаем его куда-то ещё и потом синхронизируем. Разложу вопрос по частям.


Первая часть была про то, что в React есть API unstable batched updates и если внутри вызова сделаешь много substates, React всё равно пройдет по дереву один раз, и не будет каких-то странных несоответствий, то, что назвали Zombie children. Это проблема, когда компонент где-то внизу и у него состояние не соответствует чему-то, что он получил по props, потому что другой компонент над ним ещё не обновился.


Эта проблема уходит, если ты делаешь батчинг. Конкурентный режим включает батчинг для всего. Unstable batched updates можно спокойно использовать в любой библиотеке, мы его используем. Это плохой пример, его не нужно было называть unstable, но сейчас уже глупо его переименовывать, потому что в конкурентном режиме это просто default.


В конкурентном режиме он вообще ничего не делает, потому что там всё всегда батчится и даже агрессивнее, чем обычно. Поэтому этой проблемы там нет.


Проблема с синхронизацией, если ты настаиваешь на том, чтобы держать UI State не в React, а где-то ещё, и при этом это не кэш (потому что с кэшами у нас будет своя история).


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


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


Поэтому одно из них — если ты выносишь именно UI State, не кэш, из React в какой-то store, то у нас будет несколько стадий, как с этим интегрироваться. Первый вопрос — мутабельный ли store или нет, потому что если иммутабельный, то всё прекрасно интегрируется.


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


Они необязательно будут работать с мутабельными сторами. Но у нас есть план добавить другой API — если всё равно нужна мутация UI-стора вне React вместо того, чтобы просто использовать React state, мы добавим другое API, которое позволит это делать, если ты реализуешь несколько дополнительных хуков в своей библиотеке.


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


О нормализации данных на клиенте


— Продолжая тему внешних источников данных, что ты думаешь о нормализации данных на клиенте, насколько это нужно? Популярные библиотеки для фетчинга, ReactQuerry, UseSWR, не нормализуют данные, в отличие от того же Apollo-клиента.


Интересный вопрос, мне кажется, в нём зарыт какой-то более глубокий вопрос. Какая цель нормализации или денормализации? Их несколько.


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


Но нормализованный кэш как в Apollo или Relay, c GraphQL, если его вручную не менеджерить, — это ещё как-то терпимо. Но это накладывает ограничения на структуру данных, и вносит сложные вопросы в духе «как работает инвалидация вещей, связанных в этом кэше?», «сколько они живут?».


В Redux issue tracker меня года четыре назад спросили: «а как часто вещи будут пропадать, если я их держу в Redux». Ну, как — перезагрузишь вкладку. Тогда как-то более наивно про всё думал.


В этом смысле нормализованный кэш — это то, что люди ассоциируют со state management, особенно если ты его делаешь вручную. Тогда понятно, почему используешь Redux или что-то подобное, какую-то сложную библиотеку, которой доверяешь это делать, как в Apollo.


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


Но если это данные сервера, то окей. Например, мы в команде React пришли к мнению, что оптимистичные апдейты, например, если делаешь пост и он тут же показывается — это на самом деле плохо, потому что это дурачит юзера: он может подумать, что всё запостилось и уйти со страницы.


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


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


О Redux


— В твоих постах периодически видно пассивно-агрессивное отношение к Redux. Почему он тебе так не нравится, учитывая, насколько удобные обёртки написали мейнтейнеры, Redux toolkit и насколько улучшилась документация?


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


Он как тест Роршаха, каждый видит там то, что видит.



Поскольку Redux ничего не предписывает, это просто присваивание переменной. И что? Он не отвечает на вопросы, не решает никаких сложных проблем. Он вносит проблемы — что я там держу, как оно там хранится, как это описывать и так далее, но не дает решения, а просто даёт ощущение, что есть какая-то коробка, куда можно что-то класть. А что — зависит от каждого проекта.


Обычно получается, по крайней мере, в React-приложениях, что выстраивается параллельная иерархия — есть React-дерево, есть Redux-дерево. И люди, которые придумали React, довольно мудро его придумали, что в любой момент можешь скопировать кусочек, вставить, и они не будут зависимыми друг от друга.


А если есть дерево, которое общается с Redux, и тут нужно сделать два независимых дерева — удачи.


Люди используют его, чтобы реализовать кэш, потому что в React нет first class-концепции кэша, но каждый реализует это по-своему. Решать проблемы сложно, поэтому они обычно нерешенные. Получается мешанина из boilerplate-кода.


Я не против boilerplate-кода, который имеет какой-то смысл — количество строк меня не пугает. Но это концептуальный boilerplate, когда разведены вещи, которые на самом деле тесно связаны. Короче, не нравится мне это всё!


Я думаю, что в целом это как со всеми такими вещами — я не говорю, что его не надо использовать и всё такое. Мы все экспериментируем. Я вот поэкспериментировал, сделал какой-то прототип для доклада, и потом получилось, что на этом все приложения пишут.


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


Что-то как-то решается, но я думаю, что мы постепенно будем закрывать потребности какими-то действительно продуманными решениями. Дело не в том, что тебе не надо использовать Redux. Дело в том, что хочется верить, что для каждой потребности у нас будет что-то лучшее, что тебе не надо будет сваливать всё в один объект куда-то наверх.


Redux — это как место, куда ты кладёшь то, что ты не знаешь, куда положить. Хочется, чтобы для всего нашлось место, и тогда туда будет нечего класть.


— Я на самом деле очень жду каких-то новых решений, в последнее время всё же использовала Redux, потому что это проверенный метод. Из способов, чтобы удобнее справляться с данными, ничего больше Redux не понравилось. Плюс для него есть удобные инструменты, можно всё в браузере посмотреть, но буду только «за», если появится что-то более интересное с вашей стороны для решения вопроса.


В Facebook разрабатывается state manager Recoil. Это просто ещё один независимый проект внутри компании или решение, на котором вы тестируете и которое когда-нибудь хотите внедрить взамен Redux?


Нет, это совершенно независимый проект. Он разрабатывался для конкретного продукта, потом некоторые другие продукты начали его использовать. Мы общаемся с автором, и есть возможность, что какие-то идеи из Recoil найдут место в React. Не то, что Recoil станет официальным… Опять-таки, я не знаю, что такое state management.


Есть технические вопросы, которые Recoil решает лучше, чем то, что у нас есть. Вопрос в том, какая часть фундаментальная, что должно быть в самом React. Мы про это общаемся с автором. Возможно, он поработает над каким-то proof of concept, чтобы посмотреть, на что это похоже, если бы это было в React. Но никаких гарантий. Мы активно следим за всеми state managers, что появлялись за последние пять лет.


О прошлом React


— У нас было про перспективу, про state managers, давай в ретроспективе. Была ли какая-то работа над React, куда компания вкладывала кучу сил, над чем-то работала, но оно не взлетело? Какая-то тупиковая ветка?


Мне трудно оценить количественно, но тоже вопрос, что такое тупик. Тупиков было довольно много, и они информируют… Приведу дурацкую аналогию: это как Бильбо не прибил Голлума, а потом тот сожрал кольцо, и кольцо в итоге всё же упало, и он свою роль выполнил.


У нас обычно выполняют такую роль проекты, которые не срослись. Например, был прототип React, который работает как в Worker, на другом потоке, и в 2014 году все думали про многопоточность. По нашему опыту, это не особо интересно.


Потому что UI должен отвечать сразу, для многих событий должен быть preventDefault(), который должен быть синхронно. Похожая проблема в React Native: он всегда асинхронный, многие вещи трудно сделать.


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


Это была тупиковая ветка, но она отчасти информировала редизайн React Native. И некоторые вещи, связанные с дата-фетчингом, которые делаем сейчас, отчасти истоки берут из некоторых идей того прототипа.


Ещё пара людей из React-команды существенное время вложили в Prepack — экспериментальный компилятор JavaScript в JavaScript, который исполняет JavaScript во время билда. Что достаточно странная идея (как и многие идеи Себастиана). Проект не выгорел — слишком сложно, как вскипятить океан.


Но пока мы его делали, узнали, что очень трудно делать какие-то серьезные оптимизации компилятора с классами. Например, трудно два компонента смёрджить в один. С функциями это легко или хотя бы понятно, как делать. С классами никогда не знаешь. Инстанс куда-то убежит, всё — ты не знаешь, кто какими полями на нём пользуется, какие методы на нём. Небезопасно что-то переименовывать, и много таких примеров.


Отчасти это было одним из векторов вдохновления для хуков: если делать какой-то очень умный компилятор, это гораздо более разумная для него платформа. И с тем же Suspense, что мы сейчас делаем — это тоже отчасти вдохновлено тем, что мы пытались делать с Prepack, но тогда поняли, что у него плохие характеристики и трейд-офф неправильный.


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


О хуках


— Как раз-таки ты упомянул про хуки. Я встречала множество статей и обсуждений, что людям не нравятся хуки, что люди фрустрированы, что у них «love-hate relationship». Понятно, что всегда найдутся недовольные какими-то решениями, но все же спустя время — довольны ли вы своим выбором, было ли это удачное решение?


Хороший вопрос. Это интересно: даже когда я сам пользуюсь, у меня тоже бывает такое, что «вот это клёво, а это бесит». И я ни за что не вернулся бы к классам.


Нам тоже было интересно, что люди думают и как у них ощущения. Проводили опрос, собрали где-то 15 тысяч ответов. Там было два вопроса. Один вопрос «предпочитаете ли вы хуки, классы, когда как или “не знаю”», и 70% выбрали, что предпочитают хуки, 10% «когда как», 7% — классы и остальные не определились.


Другой вопрос был — нужно ли использовать в продакшене хуки или классы, и у хуков около 70% было, у классов — 50%. И это всего спустя два года после выхода. Уже другая парадигма


Понятно, что когда мы выпускали хуки, говорили, что всё нормально, ничего не меняется. Но по сути это уже другой API, который более правдиво выражает, что такое React, и у него есть лёгкие стороны, есть сложные. Это факт, мы не пытаемся это скрыть. И сейчас переписываем документацию, думаю, будет очень прямолинейны.


Всякие вещи типа useEffect — это hard mode у React. И у этого есть причина — не потому что мы не можем придумать лучший API или из-за идеологии, а потому что сделать хороший мостик между декларативным и императивным миром сложно.


Приходится думать про комбинации вещей, которые могут произойти, но сходу не придут в голову — о race condition, о том, что какие-то вещи могут открыться или закрыться, данные могут поменяться на середине того, как что-то делаешь. И с классами было проще писать код, который как бы работает, но ломается в edge case.


А хуки заставляют тебя обрабатывать такие случаи. И там парадигма, что мы сразу ломаем код. Это бесит, особенно если используешь низкоуровневое типа useEffect— ощущение, словно решаешь уравнение. Нужно понять, как так скомпоновать, чтобы всё работало. Но когда получается, понимаешь, что реально работает — и для редких случаев, и для типичных.


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


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


С тем же конкурентным режимом не так интересно. Но то, что мы можем построить на тех ограничениях, которые он вводит — фичи, связанные с анимацией, с дата-фетчингом, это уже интересно.


И хуки — гарантия того, что всё будет работать. Потому что они так математически построены. В целом они чем-то бесят, когда перед тобой ставят задачу. Но то, что их можно использовать много раз — для нас важнее.


О холиварах


— Уведу от хуков немного в холивар, потому что то, что ты говоришь о хуках и сложном варианте React очень близко и Vue.js тоже, потому что у Vue есть новый composition API — hard mode Vue. Его очень часто сравнивают с хуками. Даже сейчас в чатике выступления вижу зрительский вопрос о том, что есть вещи, которые Vue утаскивает у React, есть то, что React утаскивает у Vue. Есть куча вопросов, какой фреймворк лучше использовать, кто у кого что утащил, чего нет, а что у кого есть. Тебя не утомила тема о войнах фреймворков и конкуренции?


Тут несколько вопросов. Про всякие бенчмарки — они, как правило, дурацкие. Самое глупое, что они показывают, что происходит, когда у тебя совсем нет кода. Зачем тебе фреймворк, если у тебя нет кода? Если у тебя много кода, приложение им обрастает, процент, который измеряет бенчмарк, погоды не делает. Погоду делает то, что происходит на макроскейле, и здесь у разных фреймворков разные подходы.


Например, Svelte считает, что генерировать больше кода — это окей, зато там очень таргетированные апдейты. Vue считает, что это окей — оборачивать данные в какие-то обёртки, и это того стоит, хотя мы тратим больше времени на тот же mount, потому что там будут какие-то апдейты.


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


Это разные трейд-оффы.


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


Это в целом. Всегда есть парадигма у каждого — у Svelte, у Vue, они на самом деле похожи, у Angular и React какая-то парадигма. И всегда будет один фреймворк каждой парадигмы, потому что у каждой парадигмы есть свои сильные и слабые стороны, на которых можно что-то интересное строить.


Поэтому конкуренция будет, есть разные идеи, и у каждой есть свои сильные стороны. Холиварные статьи меня никак не колышут, мне всё равно — если можем сделать React лучше, меня это устраивает.


— Это скорее было чтобы поделиться болью, потому что это точно такие же вопросы, которые любят задавать и представителям Vue, и представителям Angular. Теперь и у тебя спросили.


— Насколько я знаю, Vue уже переписали на TypeScript. Когда, может быть, React перепишут с Flow на TypeScript?


Зачем? Я не то, чтобы какой-то адский фанат Flow, мне и то и другое по большей части по барабану, но это звучит как проект, в котором я не вижу смысла. На definitions, которые публичные, это никак не влияет. Наши внутренние типы всё равно будут другие, потому что то, как ты используешь React, отличается от того, как он реализован. Поэтому это звучит как большой проект, который не даёт для нас никакого выхлопа, и главное — для наших юзеров.


Для нас это может дать лучшую IDE-интеграцию, но я не пользуюсь IDE, так что мне всё равно. Контрибьюторов у нас не так много, контрибьютить в core довольно сложно, потому что нужно много контекста для понимания, почему определенные вещи сделаны таким путём. Поэтому смысла, наверное, нет. Если и будем переписывать, то как-то более фундаментально, на тот же Rust или что-то такое. Конкретных планов пока нет.


О личном


— Может быть, поговорим о чем-то более личном? Я знаю, что Наташа недавно переехала в Амстердам, я недавно уехала из Лондона. Хотелось бы спросить у Дэна — может, ты что-то посоветуешь людям, которые хотят переехать и работать за границей? Может, есть какие-то моменты, на которые, как ты считаешь, нужно обратить внимание?


У меня всё случилось очень быстро, я сам не особо понял, что произошло. Мне было гораздо проще, потому что это большая компания, с агентством по релокации. Есть люди, которые тебя пилят: пришли тот документ, другой. В этом плане проще, чем если бы я сам всё делал.


Поэтому мой совет — начинать с большой компании, а там уже, когда виза есть, можно перевестись.


— А по коммуникации с командой, с людьми другой культуры возникали сложности?


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


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



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


Отчасти да, есть какие-то вещи, над которыми мы работаем. Они набирают обороты, мне интересно за ними следить и немного тоже помогать. Плюс мы с Рейчел (Rachel Nabors — прим. ред.) переписываем с нуля документацию React.


Но у меня всё равно чувство «вверх-вниз, вверх-вниз», когда в один день кажется классным, что написали, а в другой — кажется стрёмным. Чистый лист, вот это всё. Думаю, что сейчас время сложное: я привык, что можно как-то развеяться, куда-то пойти, съездить. А сейчас всё одинаковое, и это немного задолбало.


— А в Лондоне сейчас локдаун продолжается?


Да, сейчас собираются снимать, но вместо него вводят другой, с какими-то уровнями. Как-то всё ужасно некомпетентно, мне кажется.


— Ты упомянул документацию. Как человек, который работает над документацией в Vue, не могу не спросить — ты сказал, что вы всё переписываете с Рейчел, вы только вдвоем работаете над докой? Как вообще организована работа над докой? Кто что пишет?


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


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


Я не знаю, насколько эффективно построен процесс. Мы написали аутлайн, какие темы от каких зависят, что нужно поставить вперёд, у нас есть список тем, где-то 40–50. Пока мы делаем так: я пишу болванку, которая минимально описывает тему и даёт какие-то челленджи, чтобы проверить, понята тема или нет.


Потом Рейчел её переписывает, потому что я пишу суховато, или выдвигает предположения, что человек что-то уже знает. А потом я ещё раз делаю техническую правку — и вот у нас такой пинг-понг, пока обоим не понравится.


— Это очень похоже на то, как мы писали доку Vue 3. Мы тоже держали её в закрытом репозитории, так что я полностью разделяю эту идею. Хотя в какой-то момент люди начали комплейнить, но это была новая версия. А тут версия та же.


Да, только когда мы её закончим, там уже может быть новая версия (смеётся).


— Это долгосрочный проект, но есть ли очень приблизительная оценка, когда планируете закончить?


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


Этот разговор состоялся на ноябрьской HolyJS — а мы сейчас вовсю готовим следующую HolyJS, которая пройдёт 20-23 апреля. Как в случае с этим интервью некоторые вопросы были от зрителей, так и на новой конференции важной частью будут чаты и видеосессии, где зрители могут пообщаться со спикерами и друг с другом. Часть программы уже известна — увидеть описания докладов и узнать всё остальное можно на сайте.