Как стать автором
Обновить
9
66.2
Андрес Ковалев @andres_kovalev

Разработчик

Отправить сообщение

Так и что в итоге произойдет, если я вызову postData() где-то за пределами реактивного контекста? Он выкенет промис, который потом в итоге отменится? Или вернет результат работы getJSON(url2)? Я ожидаю, что:

  • либо изменение this.flag приведет к инвалидации postData() и, соответственно, отмене/реджекту выкинутого им промиса;

  • либо изменение this.flag приведет к инвалидации postData(), в результате чего он будет перезапущен, но выкинутый промис будет переиспользован и я в итоге получу значение getJSON(url2);

  • либо после завершения работы getJSON(url1) компьютед будет вызван еще раз, но т.к. this.flag изменился, выполнение пойдет по второму пути, запустится getJSON(url2) и уже после его завершения я получу его результат.

А что помешает мне вызвать postData() вне реактивного контекста? Вы же выше писали, что такое возможно и в этом случае мне нужно будет самостоятельно поймать промис и дождаться его выполнения?

Еще один вопрос: а обрабатываете ли как-то ситуации, когда в процессе выполнения компьютеда вне реактивного контекста какая-то из его зависимостей меняется? Что-то вроде такого:

class Store {
  $mems postData() {
    // this.flag - реактивное значение
    return (this.flag) ? getJSON(url1) : getJSON(url2);
  }
}

В данном примере что вернет postData() вызванный вне реактивного контекста, если значение this.flag изменится до завершения вызова getJSON()?

Upd: заглянул в реализацию и оказалось, что для того чтобы "магия" автообновления работала, библиотека патчит React и этот функционал не будет работать в React 19.

Мне нравится концепт сигналов, но такое решение не кажется хорошей идеей.

А есть например forwardRef из React.

Штож, никто и не говорит, что все решения принятые командой React идеальные.

Я так понимаю тут речь про хранение локального состояния и функций в mobx.

Тут речь о том, что проблем с HOC'ами в MobX нет. Конкретно относительно Ваших тезисов хотя бы потому, что MobX не предлагает создавать HOC'и, он лишь предоставляет один для превращения обычных компонентов в реактивные. Это как memo() в React, который делает компонент чистым.

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

Вот тут с удовольствием бы поспорил предметно, с примерами.

Выше по комментариям были разные примеры кода на MobX. Если их не достаточно - придумайте задачку и возможно мы с Вами сравним решения.

Кстати, по поводу HOC'ов - они не обязательно обязаны создавать дополнительную ноду в React дереве)

Вы ошибаетесь. Именно классы - по сути раннее связывание данных и методов (не интерфейса и методов), и наследованные - и есть ООП.

Даже в самом названии ООП нет слова "классы", речь об объектах. А наследование - лишь один из принципов. Можем хоть к Википедии обратиться. Ну или кидайте свои более правильные ссылки)

Скорей даю подсказку

Да не, апеллируете к авторитету =)

А язык Go как раз и создавали, чтобы уйти от проблем ООП языков, и успешно с этим справились - то есть над этим точно задумывались

Откуда информация? У Вас в предложении отдельные части не согласуются.

Как и тех кто делал React - эти сами прошли по всем граблям, и каждый сегодня может сам сравнить ООП vs функции

А разве на функции перешли не потому, что они лучше ложились конкретно на концепцию файберов (это не точно, ссылку искать лень).

Думаю никто в здравом уме сегодня не выберет классы как основной способ создания компонент.

Еще раз напомню, что кроме React есть другие инструменты, например Angular, в котором на классах все прекрасно организовано.

А в музыкальных трендах вашей соцсети откровенное 💩, и?) Мне бросить Pink Floyd, Кино, ZZ Top и мн. др. и пойти слушать нашу эстраду?

  1. Субъективное суждение.

  2. Не понимаю, как оно относится к моему тезису про то, что Java прекрасно живет с ООП и никто там даже не пытается от него убежать. Выглядит как эмоциональный выброс.

Вы руководствуетесь в суждениях хайпом

предсказываю что скоро будет что ООП - отстой

Выше в другом комментарии я писал, что мне кажется что текущая ФЕ повестка как раз давно уже звучит как "ООП - отстой". Лично я каждый раз спрашиваю того, кто так заявляет в чем причина такого мнения. Еще ни разу не получил конкретного аргумента. Ждем Вашу статью.

Чем лучше технология, тем она проще, понятнее, и в ней меньше "магии" (KISS)

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

Когда то и классы были чуждой концепцией.

Новой, но не чуждой. Мой тезис был про то, что хуки - априори не новый подход, а костыль.

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

Вы тут имеете в виду непосредственно разработчиков библиотеки, или тех кто ей пользуется? Мотивы первых мне не известны, а вторые думаю ясно почему "моментально пересели".

Кстати, Вы же знаете, что функции до сих пор не могут полностью заменить классы?

Забавно, что здесь "магию" вы уже не защищаете.

Это я не понял, про что Вы. На всякий случай поясню - я не за/против магии, я лишь говорю, что "магия" = "я не понимаю как оно там работает" и этот принцип для разных людей работает на разных масштабах. Как Event Loop - кто-то знает как он работает в деталях, а кому-то это не интересно.

Нет, они могут добавлять состояние и эффекты.

Вы не до конца прочитали этот пункт:

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

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

Да что вы говорите. Тот же connect у redux наверное очень плохой, как же его переписать чтоб ничего не прокидывал

О чем тут речь? Что Вы там прокидываете из одного HOC'а в другой?

В современных языках типа Go и фреймворках типа React (да это фреймворк) от ООП отказались не просто так.

  1. Насколько мне известно, в Go не отказались от OOP. Там есть структуры и множество способов выстраивать отношения между ними. Наследование - не единственный признак ООП;

  2. В данном случае вы апеллируете к авторитету. С тем же успехом можно сказать, что "в современных языках типа Rust и фреймворках типа Angular от ООП не отказались не просто так";

И создатель Java признавал что зря добавил классы

Тем не менее одна из самых популярных платформ и большинство инструментов и библиотек в ней используют именно ОО подход и паттерны.

Я не говорю, что ООП лучше ФП, но и заявлять обратное тоже считаю некорректным. У каждого подхода свои плюсы/минусы и область применения.

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

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

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

  2. Тот же контекст, который Вы упоминаете, при неправильном использовании приводит к большому количеству лишних обновлений UI. Если попробуете погуглить, то можете даже обнаружить что многие люди в принципе считают контекст плохой практикой, "потому что он приводит к обновлению всего и вся" (на самом деле это не так).

По поводу HOC - они

Интересно, но когда React был на классах - HOC'и были крутым паттерном переиспользования логики, но как-то со временем все извратилось и сейчас то же самое говорят про хуки, хотя на самом деле:

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

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

  3. Используя хуки useEffect(), useCallback() и useMemo() вы порождаете новую функцию (и замыкание, которое кстати дороже класса) на каждый рендер, даже если она в итоге не будет использована. Это дополнительно нагружает GC. Лично я не стану заявлять, что лишние статичные ноды в React-дереве бьют по производительности сильнее.

  4. Более того, проверка зависимостей этими хуками не бесплатна (как и внутренняя логика их вызова в React) и происходит каждый рендер. Именно поэтому использование useMemo() и useCallback() получается таким противоречивым - вроде по логике нужно заворачивать в них все, что потом попадает в пропы дочерних компонентов, а вроде неизвестно - мемоизированы ли дочерние компоненты и будет ли это иметь смысл?

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

  6. Если Вы прокидываете данные между HOC'ами, вы скорее всего делаете что-то неправильно (например, разделяете то, что не должно быть разделено). То, что Вам приходится передавать значения между хуками - это как раз следствие того, что на этот костыль наложили слишком много ответственности. Например, useMemo() и useCallback() можно заменить простым memoize-one , который кстати можно вызывать в условиях и циклах.

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

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

  return (
    <>
      <div>{`count = ${count}`}</div>
      <div style={{ background: 'red', padding: count.value }}>1</div>
    </>
  );

В данном примере содержимое просто не обновляется =( Первое еще можно пофиксить с помощью useComputed():

const str = useComputed(() => `count = ${count}`);

А вот чтобы обновлялся второй элемент, придется использовать useSignals(), но в этом случае оптимизация просто перестанет работать (сообщения начнут сыпаться в консоль).

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

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

Как это без ведома - посмотрите в консоль, там будет вывод "render" на каждый рендер.

Более того, вы можете сделать к примеру так

Ну так в данном случае вы не используете значение в основном компоненте, но дочерние будут рендериться каждый раз при изменении. Тут ничего удивительного. Грубо говоря, любой менеджер состояния так себя поведет, просто в одних за использование считается непосредственное чтение (MobX, Reatom, Recoil и тд), а в других - использование хука (Redux, Effector и тд).

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

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

есть мнение, что все что сделано на классах по умолчанию плохо спроектировано

Присоединюсь к другим комментаторам и с удовольствием послушаю аргументы в пользу этого тезиса. Скиньте ссылку на статью, когда будет готова.

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

...отсутствие магии и оберток HOC

А еще подскажите, что в Вашем понимании "магия"? И почему она и HOC'и это плохо?

Я не эксперт по сигналам преакта, но кажется что чтобы отделные нативные ноды могли быть реактивными, нужна какая-то логика в самой библиотеке (как, например, у spring есть свои версии нативных элементов) или трансформация при сборке. Решил глубоко не копать, а просто проверить ваше предложение на практике (песочница), и там ожидаемо обновляется весь компонент - можете сами проверить. Я ничего не упускаю?

Cигналы из Preact действительно крутая штука. Я полагаю они не рассматривались по нескольким причинам.

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

Во-вторых, если сравнивать с MobX, то тут у последнего для нас есть несколько преимуществ:

  • Объектный стиль получается гораздо лаконичнее - нам не нужно явно писать singnal/computed в подавляющем большинстве случаев, получается обычный JS +минимум бойлерплейта.

  • MobX все же популярнее и имеет более обширное комьюнити. Мне также не известно крупных проектов, использующих сигналы Preact без самого Preact (тут возможно информация устарела).

Со стороны кажется, что каждый из вас по-своему прав:

  • В примере есть autorun(), использующий значение реактивной переменной. Благодаря тому, что обновления распространяются синхронно на каждой итерации происходит вызов реакции. Т.е. реактивная система действительно задействована.

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

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

Думаю, в статье есть ответ на этот вопрос =/

Добавлю к этому тот факт, что указанные материалы к сожалению отсутствуют в поисковой выдаче по запросу "проблемы effector", что сильно уменьшает вероятность того что они будут учитываться в процессе принятии решения 😔

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

  2. А при повторном вызове (после резолва промиса)? Кеширование не привязано к реактивном контексту?

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

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

Ссылки на материалы, которые мы непосредственно использовали в исследованиях или при подготовке статьи указаны по месту (там, где идет соответствующее повествование).

Под "статьей трехлетней давности" Вы имеете в виду ссылку на пост про проблемы Effector? Кмк, возраст поста тут не имеет значения, он скорее призван показать как трудно было во времена нашей истории (да и сейчас) найти любую полезную альтернативную информацию конкретно про Effector.

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

Если API описанное в статье еще актуально, то для создания асинхронных компьютедов все асинхронные функции превращаются в псевдо-синхронные работающие на Suspense API. Пара вопросов по этому поводу:

  1. Чтобы обработать исключения Вы каждый раз в блоке catch вставлять проверку на промис (может есть смысл сделать утилиту типа rethrow()?). Учитывая, что синхронная функция внешне не отличается от псевдо-синхронной - Вы такие проверки делаете всегда во всех catch'ах?

  2. Можно ли использовать одну и ту же функцию внутри одного компьютеда несколько раз? Для каждой нужно создать псевдо-синхронную версию или кеширование данных для каждого набора аргументов происходит на уровне компьютеда?

    const getJSON = sync(async function getJSON() { ... });
    
    class Store {
      // будет ли такое работать?
      $mems postData(id: number) {
        const content = getJSON(`/post/${id}`);
        const comments = getJSON(`/comments/${id}`);
    
        return { content, comments };
      }
    }
  3. Если кеширование происходит на уровне компьютеда, то что если я обращусь к нему из-за пределов реактивного контекста?

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

  5. В рамках выполнения компьютеда отличаются ли как-то "перезапуск из-за обновления зависимостей" от "перезапуска после резолва промиса"? Видел в Ваших примерах использование sleep(500) для реализации debounce'а - это тоже псевдо-синхронная функция? Если да, то не значит ли это что задержка сработает только один раз при первом обращении к компьютеду?

Что прям все?) Или 99%?) А если взять самый очевидный пример - Angular, там тоже классов избегают?

Опять же кто?) Да, такие люди есть, но с чего это их мнение правильное?

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

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

Получается вы попали в классическую ловушку, увидели что где-то кто-то делает вот так, увидели какие-то статьи и всё, ваша картина мира сложилась и вы поняли что да, это именно то, что нужно)))

Примерно так и было. В статье я как раз пишу, что мы выбрали Effector отчасти "на волне хайпа". И это было ошибкой.

Неужели так сложно понять что это гуано не пробуя его на вкус? Например по виду и запаху? Это же сразу очевидно когда смотришь на код эффектора и на этот подход.

Как я упомянул в статье, мы делали выводы из слишком маленького объема информации и простого примера. Грубо говоря, "не почуствовали запах в таком масштабе".

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

Мы внедряли Effector во всем ВКонтакте - это наш основной проект =) Внедрением занимались 2 десятка команд целый год. Сейчас переводим на новую архитектуру, которая для управления состоянием использует MobX.

1
23 ...

Информация

В рейтинге
86-й
Откуда
Ухта, Коми, Россия
Дата рождения
Зарегистрирован
Активность