Pull to refresh

Comments 11

А что насчет debouce?
Что насчет race condition?

А почему просто не использовать классику?

import { makeAutpObservable } from "mobx";

class SomeState {
  fetching = false;
  error = null;
  item: IItem = null;

  constuctor() {
    makeAutpObservable(this);
  }
  
  fetchData = async () => {
    this.fetching = true;
    const ah = asyncHelpers(this.fetchData);
    // Debounce + no race condition 300ms
    if (!await ah.debouce(300))  return;
  
    try {
      const item = await new ApiReq(`GET /api/v1/item/${this.itemId}`)
                                        .withCahe(60) // кэш 60сек
                                        // для отмены запросов
                                        .withAbort(ah.abortControllersArray)
                                        .send()
      // race condition check
      if (!ah.stillActual()) return;

      const itemComments = await new ApiReq(`GET /api/v1/item-comments/${item.commentsId}`)
                                        .withCahe(60) // кэш 60сек
                                        // для отмены запросов
                                        .withAbort(ah.abortControllersArray)
                                        .send()
      // race condition check
      if (!ah.stillActual()) return;

      const data:IItem = {
        ...item,
        comments: itemComments
      }

      this.item = data;
      this.error = null;
    } catch (e) {
      // race condition check
      if (!ah.stillActual()) return;

      this.error = e;
    } finally {
      // race condition check
      if (!ah.stillActual()) return;

      this.fetching = false;
    }
  }
}

Ну и дальше в компоненте

const MyList = observer(() => {
  useState(() => { someState.fetchData(); });
  if (someState.fetching) return <Spinner />

  return (
    <div className={styles.list_container}">
      {someState.map(item => <div className={styles.list_item}>...</div>)}
    </div>
  )
});

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

Что ж, по порядку:

  • Здесь использована магия (не все любят магию) mobx с ООП (многие считают эту парадигму неудачной), обертками компонент (увеличение vdom, стектрейса). Вынесем это за скобки ибо холивар разводить не хотелось бы.

  • Использована какая то неизвестная мне функция asyncHelpers для debounce и отмены запросов.

  • Использован неизвестный мне класс ApiReq (опять ООП) в тч для кэширования запросов. Причем в данной реализации нет возможности принудительно запустить обновление данных, например через Pull to refresh - данные минуту всегда будут приходить из кэша. Неприятный баг UX.

  • fetching каждый раз переходит в true, а в конце в false, тем самым показывая на короткое время спинер даже если данные уже закэшированы - UI баг. Возможно потребуется серьезный рефакторинг чтобы это исправить, если не полный отказ от данной архитектуры. Тут нужно смотреть на реализацию ApiReq.

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

  • Данные item можно показать еще пока не погрузились комментарии, но не в данном примере. Потребуется рефакторинг.

  • Легко ли прикрутить, например, персистентность, SSR и тп?

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

По поводу debounce в RRC:

  • Для query используется throttling - пока идет запрос с определенными параметрами, другие с теми же параметрами отменяются.

  • Для мутаций используется debounce - каждая следующая мутация отменяет предыдущую, если та еще не завершилась. Для этого вторым параметром в мутации передается abortController.signal.

Race condition там нет.

  • Здесь использована магия (не все любят магию) mobx с ООП (многие считают эту парадигму неудачной), обертками компонент (увеличение vdom, стектрейса). Вынесем это за скобки ибо холивар разводить не хотелось бы.

Object getter/setter магия?) Ну ок) Магия так магия)))
https://stackblitz.com/edit/vitejs-vite-fkgny3?file=src%2Fmain.ts&terminal=dev

  • Использована какая то неизвестная мне функция asyncHelpers для debounce и отмены запросов.

Ну да, она легко имплементируется самостоятельно, ее АПИ же видно и понятно как она устроена и работает

  • Использован неизвестный мне класс ApiReq (опять ООП) в тч для кэширования запросов.

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

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

Так то вообще легко, например можно в fetchData добавить аргумент force который в withCahe передаст null, что будет означать что кэш мы игнорируем и делаем запрос, опять же т.к. реализация своя можно всё что угодно добавлять.

  • fetching каждый раз переходит в true, а в конце в false, тем самым показывая на короткое время спинер даже если данные уже закэшированы - UI баг. Возможно потребуется серьезный рефакторинг чтобы это исправить, если не полный отказ от данной архитектуры. Тут нужно смотреть на реализацию ApiReq.

Т.к. MobX сконфигурирован на асинхронные реакции(включая автобатчинг), а они запускаются путём setTimeout, а внутри функции у нас промисты(микротаски), в момент испускания реакции значение fetching будет неизменным true и никаких спиннеров на короткое время не будет.
Вот как выглядит данная конфигурация

Вот как это в действии https://stackblitz.com/edit/vitejs-vite-vslbhn?file=src%2Fmain.ts&terminal=dev

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

Вы о чем вообще? Пример просто из потолка написан, тут полная свобода, нужна нормализация - легко, не нужна - легко. Причем тут кол-во запросов к серверу? С чего вдруг что-то переписывать надо? Обрабатывайте данные как душе угодно.

  • Данные item можно показать еще пока не погрузились комментарии, но не в данном примере. Потребуется рефакторинг.

Легко, просто код чутка измените под эти нужды и всё, элементарно же.

  • Легко ли прикрутить, например, персистентность?

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

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

Т.е. по вашему все вокруг идиоты, включая меня. И решения написанные нами это дно и их лучше не использовать. А вот если использовать библиотеку которую кто-то там написал(в том числе вы), вот уже совсем другое дело, там настоящий уровень и все так прекрасно и удобно. А если мне/Васе/Пете и т.п. не нравится то, что есть? Как быть? Ну не смешите такими выводами странными. Такие "выводы" канают только для начинающих.

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

Вы сами придумали "проблемы", которые кстати решаются по щелчку пальца, ибо описанное вами поведение нужно конкретно вам, а конкретно мне нужно другое поведение, а конкретно Васе нужно своё поведение и т.п.

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

Да любой проект, который писали опытные люди, в том числе классическое решение это функция/класс обертка для запросов к АПИ, в том числе события, на которые можно подписаться и перехватывать запросы/модицифировать их/универсальную обработку ошибок делать/ и т.д. и т.п. Так же классика это когда компоненты, которые используются в разных местах лежат в src/components и т.п.

Из того что вы ответили получается что вы практически реализовали свой фреймворк на базе mobx, который используете от проекта к проекту, но только не оформили его в библиотеку. Что то в этом фреймворке требует изучения далеко не очевидной конфигурации mobx (как пример где fetching всегда false), что то не реализовано совсем - нормализация, пагинация, персистентность и мн. др., тот же optimistic response, и разумеется чтобы их добавить "просто код чутка измените под эти нужды и всё, элементарно же". Вот только многое из этого не элементарно и потребует больших изменений, довольно багоемких, которые в идеале стоило бы еще и тестами покрыть.

Оформите вашу идею в библиотеку, добавьте туда многое из того что обсудили и то что есть у "конкурентов", и возможно любители mobx ее оценят.

Из того что вы ответили получается что вы практически реализовали свой фреймворк на базе mobx, который используете от проекта к проекту, но только не оформили его в библиотеку.

Ну по сути да, за 12 лет из которых 8 лет чистого фронта и множество разных проектов, грех не обзавестись кучей решений и подходов на все случаи жизни

Что то в этом фреймворке требует изучения далеко не очевидной конфигурации mobx (как пример где fetching всегда false)

Не надо ничего изучать, тебе сказали 1 раз, реакции асинхронные по умолчанию, отсюда и автобатчинг, и всё, ты это услышал и понял, 5 секунд изучения)

что то не реализовано совсем - нормализация, пагинация, персистентность и мн. др., тот же optimistic response, и разумеется чтобы их добавить "просто код чутка измените под эти нужды и всё, элементарно же"

Ну вообще да, без шуток. Любой каприз, берешь и делаешь.

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

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

Оформите вашу идею в библиотеку, добавьте туда многое из того что обсудили и то что есть у "конкурентов", и возможно любители mobx ее оценят.

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

У меня другой опыт - я насмотрелся на опытных разработчиков, реализовывающих "каждый чих" самостоятельно, и все это многократно переписывал. В 95% случаев (если не чаще) все это работает плохо, забаговано, и на больших проектах не подлежит исправлению кроме как "выкинуть и написать с нуля". Про очень многие из лучших паттернов они даже и не в курсе (нормализация, optimistic response и мн. др.). Эти разработчики кстати чаще всего сами не видят никаких проблем в своем коде, и уж тем более не видят проблем в UX приложения.

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

Для начала с чего вы взяли что они реально опытные?) Например если человек Bob 15 лет работал на 2-3 проектах(а я таких знаю реально, прям лично), и человек Tom работал 5 лет на 10+ проектах, то Tom в 95% случаем будет на 3-4 головы лучшим разработчиком чем Bob который в опыте в годах аж на 10 лет больше работал. И это Tom построит гораздо более лучшую архитектуру на новом проекте, чем Bob, как минимум тупо за счет того что Tom видел в 3-4 раза больше проектов чем Bob.

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

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

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

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

Возможно вы сами себе придумали критерии и по ним оценили что всё работает плохо. А если посмотреть объективно со стороны, то по факту все работает как минимум нормально. а то и хорошо.

Эти разработчики кстати чаще всего сами не видят никаких проблем в своем коде, и уж тем более не видят проблем в UX приложения.

Зависит от ваших индивидуальных критериев к коду. Мало ли, может вы пишете вложенные друг в друга теранарки, или не делаете early return в функциях(в том числе реакт компонентах), или вместо async/await пишете цепочки .then и т.д. и т.п. И считаете что это нормально и правильно, а другой код плохой.
Хотя если быть честными самими с собой, то код, который ты читаешь первый раз сверху вниз и понимаешь что он делает, что будет дальше, какой будет результат его выполнения - это хороший код.
А если ты смотришь и не понимаешь, без документации, без задавания вопросов автору кода и т.п. - этот код плохой.
Но бывают и исключения из этих правил. в случаях когда очень сложная и навороченная бизнес логика, в таких случаях код будет плохим неизбежно и без вариантов. Где-то чуть менее плохим, где-то чуть более плохим, но суммарно плохим.

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

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

Я думаю что судя по его комментам он бы сказал то же и про mobx в угоду effector, а я бы сказал так про mobx и effector в угоду redux) А в соседней статье любители хайпа отказываются от effector после года мучений и в очередной раз выбирают технологию без должного анализа)

Вот только профессионалы как использовали redux и не имели с ним никаких проблем, так и используют. Потому что это самый простой, а значит лучший инструмент.

Sign up to leave a comment.

Articles