Pull to refresh

Ещё один способ работать с Promise для Redux

Reading time 3 min
Views 11K

Так вышло, что в данный момент я принимаю участие в разработке фронт-энд приложения (React + Redux), делающего множество запросов к REST API каждую минуту, если не секунду.


Мне надоело на каждый запрос писать REQUEST/FAILURE/SUCCESS (далее RFS) экшны, к ним кейсы для редьюсера, всё это обильно поливать тестами (ведь качество превыше всего).


Я написал очередной велосипед.


Велосипед с реактивным двигателем


Существующая проблема


Уже написано множество библиотек для удобной работы с RFS, но они предполагают детальную настройку каждого из трёх экшнов + написание кейсов для редьюсера + тесты. В основном данные библиотеки можно использовать в 100% случаев написания запросов к серверу. Такая гибкость требует написания массы кода для однотипных задач.


В проекте, которым я занимаюсь в данный момент, 90% запросов выполняют предельно простую задачу: сходи на сервер, возьми данных, преобразуй их немного, положи в state, если что-то пошло не так — жалуйся. Написаны тысячи строк тестов, по своей сути являющихся копипастой с микро-изменениями.


К сути


Доколе? Выходные перед монитором, литр кофе, шаверма на обед и ужин, немного контролируемой магии...


не открывай это

вжух


… библиотека готова.


Функция fromTo(from, to, [through]) возьмёт данные где сказано, преобразует как надо и положит в указанное место (любое в вашем state, но только если для редьюсера вы используете библиотеку immutable). Редьюсер самостоятельно (после обёртки его во fromTo.wrapper) поймёт как работать с данными и предсказуемо изменит state в соответствии с RFS экшнами (о которых библиотека позаботится сама). Тестами покрыто всё (если найдёте багов — давите несчадно или откройте тикет).


Краткое описание возможностей


Простейший способ (на нашем проекте это около половины случаев) использования fromTo экшна это:


// запрос к серверу возвращает {"mood": "happy"} , HTTP200

dispatch(fromTo(
  () => axios.get('dogs.io/good-boy'),
  ['dogs', 'goodBoy'],
));

Вот что в это время будет происходить в вашим state:


Начальное состояние


{
  ...otherReducers,
  dogs: Immutable.fromJS({}),
}

REQUEST


{
  ...otherReducers,
  dogs: Immutable.fromJS({
    goodBoy: {
      isRequesting: true,
    },
  }),
}

SUCCESS


{
  ...otherReducers,
  dogs: Immutable.fromJS({
    goodBoy: {
      isRequesting: false,
      data: {
        mood: 'happy',
      },
    },
  }),
}

Ещё для 40% случаев используется объект в качестве аргумента to и добавляется третий (опциональный) аргумент. Востальных случаях действуем по-старинке (fromTo не панацея, а удобный инструмент).


Разбор функции fromTo(from, to, [through])


Функция имеет 3 аргумента.


  1. from. Говорим где взять данные. Функция, что возвращает Promise. Будет вызвана без аргументов.


  2. to. Говорим куда сохранять данные.


    • Если использовать объект в качестве аргумента, то необходимы 3 ключа: { request, failure, success }. Это 3 координаты для данных в вашем state, куда будут сохраняться:
      request: координаты для булевого значения завершен ли вызов from (например [ 'dogs', 'isFetching', 1 ]),
      failure: координаты для данных, что вернулись при reject (например [ 'cats', 'errors', 2 ]),
      success: координаты для данных, что вернулись при resolve (например [ 'robots', 'data', 3 ]).
    • Можно ограничиться списком (например [ 'goodBoy' ]), тогда данный аргумент будет преобразован в объект { request: [ 'goodBoy', 'is Requesting' ], failure: [ 'goodBoy', 'error' ], success: [ 'goodBoy', 'data' ] }

  3. [through]. Говорим (а можем и не говорить и довериться дефолтам), как преобразовать данные, что вернулись в следствие from(). Объект может содержать один или оба из методов:
    • requestAdapter — функция, что получит на вход данные из resolve. Вернувшиеся данные будут сохранены при SUCCESS,
    • errorAdapter — функция, что получит на вход данные из reject. Вернувшиеся данные будут сохранены при FAILURE.

Итог


В данный момент на проекте мы экономим часы нашего времени и пучки нервов благодаря отсутствию необходимости писать тонны однообразного кода. Вместо 90% экшнов, редьюсеров, тестов — вызывается одна функция dispatch(fromTo(...args)). Мы рады.


Конечно, некоторое количество тестов всё же пишется, но в основном это тесты на аргумент through (обязательно) и дань TDD.


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


Хорошего дня.

Tags:
Hubs:
+8
Comments 58
Comments Comments 58

Articles