Pull to refresh
3
0
Send message

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

Хотя в своем примере тестирования тоже мокает зависимости, просто в новом подходе это делается чуть проще.

Ну а что мешает один раз написать моки для этих зависимостей и переиспользовать?

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

/router.tsx отвечает и за создание роутера, и за рендеринг

Нет, router.tsx отвечает за роутинг (единственная ответственность). А роутинг в себя включает и определение роутов, и их отображение отталкиваясь от window.location, и их композицию и тд.

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

А роутинг в одном файле - очень удобно, нежели искать по проекту кто как и где рендерится

Автор, хотелось бы услышать продолжение, статья понравилась.

Да, и почему никто не говорит, что например Реакт уже определяет архитектуру? А еще никто не говорит, что DOM и есть архитектура, древовидная. Так что любые решение по архитектуре, уже ограничены архитектурой веба и фреймворка

А тот факт, что бот Яндекса будет иметь доступ к каналу, и сможет выкладывать что угодно, и когда угодно?

Я не понял, почему когда абстрагируемся, связи остаются такими же, что и на низких уровнях (HTTP запрос между микросервисами).

Пример: На уровне класса, связи - прямые вызовы. Дальше по абстракции может быть например несколько процессов, которые вызывают друг друга через IPC. Дальше несколько сервисов через общую шину. Дальше несколько систем через gRPC.

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

Окей проблема понятно. А вот почему через typescript решили, непонятно.
Посмотрите в сторону zod (валидатор). Схема описывается через JS, но можно вывести typescript типы.
+ вам для low code платформы нужна будет валидация данных, например чтобы число было положительным, или чтобы строка была не длиннее чем X символов.
А как вы это сделаете через typescript?
Решение я описал в первом комментарии, и как будто больше подходит. А еще вопрос поддержки. Вот вы разобрались, понимаете как парсить ast дерево, но задача у вас простая, и придется поддерживать этот кому-то этот костыль....

А мне так и непонятно, какую проблему вы решили? Вы хотели использовать и typescript и proptypes, но описывать одну схему?
Если да, то не легче было просто общую схему описать, которая бы генерила proptypes, и выводила typescript схему?
То есть использовать code first, а не schema first

Да, простите, пропустил. Но мой пример лучше, так как пример в статье сломается, если дочерний компонент вызывет этот коллбэк в useLayoutEffect сразу при маунте.

А я бы еще добавил важный хук:

export const useCallbackRefed = <T extends (...args: any[]) => any>(callback: T): T => {
  const ref = useRef(callback);
  ref.current = callback;

  return useCallback<T>(
    ((...args: any) => {
      return ref.current(...args);
    }) as any,
    [],
  ) as T;
};


Зачем он нужен? Это тоже самое что и useCallback, но без зависимостей. И его можно безопасно использовать в useEffect, а еще безопасно передавать как props

А автор не подумал, что если теперь есть soft delete, то старые запросы нужно тоже переписать? Ведь запрос на сущность, которая теперь soft удалена, должен не возвращать данные, а он продолжает возвращать. И если запросы бы были переписаны, то partial index бы сработал. Тут факап сразу в нескольких местах. А еще можно было синкануться с коллегами, чтобы хоть кто-то другой знал план, и бы заметил что что-то не так в этом плане...

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

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

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

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

В React нет такой возможности

А useRef? А держать состояние вне компонента?

Я посмотрел на Fusor. В React тоже можно так сделать с помощью ref-ов. Все данные можно хранить в ref-ах, а когда нужен перерендер, использовать для этого useReducer:

const [_, forceUpdate] = useReducer((c) => c + 1, 0);
const myData1 = useRef();
const myData2 = useRef();

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

Fusor - не разделение ответственности, потому что философия React в том, чтобы реактивно показать данные, сматченные на верстку. И тут как раз и получается, одна ответственность - реактивно отображать данные, с чем React отлично справляется.

мемоизация - это не бесплатно

Согласен, но зависит от компонентов. Если в компоненте используется несколько хуков, useState/useEffect, то скорее мемоизация оправдана.

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

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

Для использования реакта в связке с MobX это отлично

Да, потому что MobX сам решает когда перерендерить. То есть, у нас теперь есть memo, который на каждый рендер будет сравнивать пропсы, и еще сама система реактивности MobX-а, которая не бесплатна.

А по аргументам

Нативный код

Ну приходится изучать MobX, и уже работать с ним:

Минимум лишней когнитивной нагрузки

Спорный момент, где-то может быть даже легче useState, а не писать целый класс

Минимум кода(отсутствие лишней лапшы)

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

Всё оптимизировано из коробки

Перерендеров нет, но зато есть потребление памяти. Повторюсь - реактивность не бесплатная.

Не привязан к реакту,

Да, это хороший пункт. И тестировать такое легче.

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

Круто, мне нравится) Только вот есть один нюанс, если мы посмотрим исходники, то увидим:
Вот тут https://github.com/mobxjs/mobx/blob/main/packages/mobx-react/src/observer.tsx#L30 вызов передается observer-у, который в свою очередь просто мемоизирует компонент:
https://github.com/mobxjs/mobx/blob/main/packages/mobx-react-lite/src/observer.ts#L160

То есть memo используется всегда! Насколько это хорошо? Наверное, если бы было очень хорошо, то разработчики React-а бы сделали компоненты мемоизированными по умолчанию. А есть у вас еще аргументы в пользу Mobx?

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

А вы не думали, что раз на хабре нету статьи про него, и вы первый, значит он и не нужен?)

Лучше не библиотеки учить, а идеи. Например, научить команду работать с ошибками и с исключениями отдельно. Понимать что есть ошибка (нормальный флоу, просто негативный), и исключение (исключительный флоу, когда все пошло не так)

А насколько вообще корректно сравнивать транспорт с библиотекой? Они хоть и похожи в названиях, но делают абсолютно разные вещи

Зачем так сложно? Откройте эти приложение в веб телеграме, посмотрите ссылку на iframe, скопируйте и поменяйте в ссылке "weba" на "android". Все, можно запускать в браузере и смотреть запросы. Смотрим запрос который отправляет кол-во кликов, копируем, и вставляем в Postman, смотрим как работает. Потом, если вы разработчик, то напишите на любом удобном себе языке простой скрипт, которые делает тотже самый http запрос

В этом голосовании можно отменить голос, и снова сделать выбор. Вот если бы вы попробовали без этой фичи, было бы ближе к истине.

1

Information

Rating
4,804-th
Registered
Activity

Specialization

Frontend Developer
Senior