Sense был задействован в React с самого начала, но нам не всегда нравился Redux. Фактически, когда мы начали создавать нашу инфраструктуру, Flux был лишь примерно обрисован на сопутствующем сайте Facebook, и сообщество использовало смесь собственных фреймворков и фреймворков с открытым исходным кодом, подобных Flux.

Мы выбрали «свой собственный», наблюдали за появлением Redux и снежным комом, и, наконец, приняли его и redux-thunk, когда нам стало комфортно с тем, насколько он созрел.

Но было несколько вещей, которые были у нашей собственной разработки Flux, чего не было в Redux + Thunk, а именно кэширование ресурсов и управление состоянием запросов, поэтому мы перенесли их.

Проблема

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

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

Соответствующий редуктор может выглядеть так.

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

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

Решение: «Введите декораторы»

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

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

Вот декоратор, который назначает KEY метод создателю действия.

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

Давайте залезем глубже в расселину.

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

Но применение декораторов в ECMAScript все еще находится в стадии разработки, и Babel не имеет для этого большой поддержки. В качестве ванильной замены мы используем Lodash flow, чтобы сделать то же самое с немного большим количеством кода.

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

Добавлять здесь комментарии бессмысленно. Мы сразу можем увидеть, что является ключом для этого ресурса, что он уведомляет компоненты о том, что он «извлекает», и что он кэширует себя. Плюс всего 7 строк. Теперь мы кодируем!

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

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

Спасибо Маркосу Охеде и Дэнни Боумену, которые разработали версии этого кода для Redux и Flux соответственно.