Comments 43
Учитывая, что с внешними стейт менеджерами работать не умеет, довольно бесполезная в реальной жизни штука. Так же большой вопрос: что там с дебагом, сурсмапы завезут? А если с тайпскриптом?
Учитывая, что useMemo и useCallback и так не решали проблему лишних ререндеров, то этот мега-компилятор-оптимизатор мало что изменит. Пара примеров:
const b = c.map( Boolean )
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const a = false
const b = Date.now()
const memoizedCallback = useCallback(() => a ? doSomething(a, b) : null, [a, b]);
А что не так? В обоих случаях на каждом рендере b - новое значение. Поэтому useCallback и useMemo будут заново пересчитываться
Проблема в том, что инструмент должен подстраиваться под разработчика, а не разработчик под инструмент. Правильный вопрос, не "а что не так", а "почему реакт не может соптимизировать этот код, чтобы не делать бессмысленную работу в рантайме"?
В первом случае значения хоть и разные, но эквивалентные, а во втором значение переменной ни на что не влияет.
Новый компилятор автоматически исправит ошибку в первом примере, обернув b
useMemo
Это ничем не поможет. Там каждый раз генерируется новый массив с тем же содержимым. Там, конечно, filter должен быть, а не map, но суть это не меняет.
const b = useMemo(()=> c.map( Boolean ), [c]);
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
В этом случае b хоть и новый массив, но он будет стабильным пока стабильно "c"
Но "с" не стабильно, в отличие от "б".
В общем случае, если "с" поменялось, то и memoizedValue должно поменяться.
Разумеется, если в "с" воткнуть значение [...c, false], то всё должно остаться как есть (с учётом поправки про filter), но тут уж на уровне библиотеки не отследить..
Сам мап тут не дорогой, проблема дальше.
Это понятно. Конкретно здесь достаточно сравнения shallow для массива "b", но это выход за пределы фундаментальных основ Реакта, где все сравнения только по ссылке. В более общем кейсе не хватит и полного глубокого сравнения. Новый компилятор, насколько я понял, будет автоматически навешивать мемоизацию, но действовать в рамках основ.
Обязвтельно тюремный жаргон в заголовке использовать?
Что "многопоточность" в 18 версии, что "компиляция" в 19 версии - решения призванные закостылить фундаментальные недостатки Реакта.
А именно:
Не разделение логики компонентов на содание и обновление
Автоматический, "умный" рендеринг/обновление
По этим причинам я решил развивать версию реакта без этих недостатоков. Всем кому интересно, прошу поучаствовать https://github.com/fusorjs/dom.
Fine-grained control over DOM updates
вручную каждый раз дёргать update()
— это не fine-grained control, а тупиковый путь
В Реакте вы каждый раз вручную дергаете setState
для обновления...
Да, но если бы, например, вместо кортежа [value, setValue]
возвращался объект с геттером и сеттером, вы бы явного setValue
не увидели бы. Собственно, примерно это происходит, например, в SolidJS, Vue или $mol. Вам бы стоило изучить prior art перед изобретением собственного велосипеда.
Фьюзор отличается от этих библиотек минимализмом и большей гибкостью. По сути, даны примитивы с методом update
из которых строится приложение.
Хуков, сигналов, жизненного цикла - не определено. Для стейта можно использовать обычные переменные, так как содание/обновление компонентов разделено. Для жизненного цикла, создание и обновление это обычные функции JavaScript, остается монтирование в ДОМ, которое по умолчанию взято из Custom Elements. Это всё можно изменить под свои нужды.
Обновление происходит только там и тогда, гды мы этого хотим. Опять же можно подключить систему автоматического diff-а и обновления как в Реакте, но зачем? Я делал небольшое сравнение по многословности, и компоненты Фьюзора выглядят "легче".
Понимаете, в вашем случае вы скрываете под «гибкостью» отсутствие полезных возможностей. В результате непонятно, почему тогда не использовать Custom elements, которые из коробки предоставляют жизненный цикл. Минимализм у вас получается самоцелью, которая при этом достигается не минимализацией кода, а выбрасыванием фич. В результате, что вы даёте разработчикам? Только JSX-рендер компонентов? Тогда почему бы им не выбрать минималистичные Preact или SolidJS, которые могут предоставить больше фич?
Требование совершать update вручную тут же обязывает программиста либо дёргать update на каждый чих, либо вручную делать какую-то систему проверки изменений. В простейшем случае это будет аналог useMemo, в который придётся завернуть все компоненты.
Такой пример: В приложении в отдельных местах могут выводиться даты. При этом пользователь может вызвать диалог настроек и поменять текущую таймзону. В вашем случае нужно будет делать глобальный update или каждый потребитель таймзоны должен будет самостоятельно подписываться на неё и отслеживать изменения?
Вы немного не разобрались, сразу понять в чем суть бывает сложно, да и над документацией надо поработать...
Фич не меньше, с Фьюзором можно осуществить те же задачи что и с Реактом, Преактом и Солидом. Нет тех фич которыя появились для закостыливаня вышеотмеченных проблем (хуки, многопоточность, компиляция...)
Нативные Кастомные Элементы не позволяют декларативно создавать и обновлять ДОМ. Фьюзор позволяет это делать используя их нативный жизненный цикл.
Проверка изменений уже встроена. Например если вы дернете
app.update()
в корне вашего приложения, то проверка побежит по всем его нодам и обновит ДОМ, но только в том случае если значение изменилось (Как в Реакте). Но самое главнояе что вы можете дернуть апдейт не в корне, а в любом произвольном месте, обновить одну только дату, и в этом случае дорогостоящий DIFF не будет бежать по всему дереву. В этом гибкость.
то проверка побежит по всем его нодам и обновит ДОМ, но только в том случае если значение изменилось
Так, отлично, Тогда что же будет проверяться и как будет производиться сравнение? Все переданные пропсы будут сравниваться через ===
? И как у вас предлагается передавать глобальное состояние типа локали или таймзоны?
вы можете дернуть апдейт не в корне, а в любом произвольном месте, обновить одну только дату
Вообще-то речь шла о таймзоне, а не дате. Ну да ладно. Кто же будет дёргать апдейт? Родительский компонент или код самого компонента?
дорогостоящий DIFF не будет бежать по всему дереву
Дереву стейта (как в ангуляре) или виртуальному DOMу?
Только динамические (функции) пропсы и чайлды сравниваются с предыдущими значениями и при разнице обновляется ДОМ в нужном месте. Через
Object.is
. Статические при создании добавляются в ДОМ и не меняются.Глобальное состояние таймзоны претендует на глобальную переменную.
Апдейт может делать как сам компонент, для своих элементов, как видно из примера "CountingButton" и "SVG Analog Clock". Так и внешний код, например: глобальный стейт из TodoMVC демки, или history для роутинга из демки Tutorial...
Дереву приложения, приложение это ваши функции - компоненты, которые используют примитивы Фьюзора, которые в свою очередь организованы в дерево. Как в Реакте. Я бы не стал это называть виртуальным домом, так как примитивы Фьюзора знают только те места ДОМ, которые нужно обновлять. И делают это они одной операцией, без предварительного запроса дом.
В общем, похоже, вам остаётся добавить реактивные примитивы, и получится аналог SolidJS
Вы можете поключить и использовать сигналы если хотите. На сколько я помню их вынесли из солида. Но накручивание лишней логики на простые операции не принесет особых бенефитов. А производительность и читабельность могут пострадать ИМХО.
А много ли реальных приложений с логикой уровня click counter или всё же в них логика значительно более сложная, что оправдывает реактивность?
Поэтому я подготовил два полноценных приложения: TODO и Tutorial в котором имплементированы основные кейсы использования. Если чего-то не будет хватать, то буду добавлять.
TODO в 2024? Серьёзно? Пример, который даже не содержит асинхронных операций?
Скоро и самого разработчика будут оборачивать компиляторам ,чтобы правильно код писал
Так а разве мемоизировать все что можно имеет смысл? Или компилятор будет судить что дешевле - мемоизация или перевычисление?
Есть же уже отличная статья от Нади Макаревич, которая показывает несостоятельность компайлера. Из 10 проблемных мест он заметил только 2. https://www.developerway.com/posts/i-tried-react-compiler
Поясняю за React Сompiler