Pull to refresh

Comments 15

Как я понял — это теоретическое рассуждение на тему. Без практической реализации. Я предоставил практический подход к решению данной задачи.
Да нет, вот вам практическая реализация(из issue по ссылке) которая вообщем-то должна работать прекрасно:

rootReducer = combineReducers({
  router, // redux-react-router reducer
    account: combineReducers({
      profile: combineReducers({
         info, // reducer function
         credentials // reducer function
      }),
      billing // reducer function
    }),
    // ... other combineReducers
  })
});
В такой архитектуре все пропертис — это обязательно должна быть функция-редьюсер.
В моем подходе в раздел «profile», например, проперти «isUse». И я бы не смог этого сделать — пришлось бы запихивать его в один из разделов «info» или «credentials», или вообще создавать новый. Все это привело бы к ненужной избыточности иерархии. Мой подход, считаю, более гибкий в этом отношении.
Вы хотите вот такую структуру в ветке стейта?
profile: {
  info: Object,
  credentials: Object,
  isUsed: boolean,
}       

Хозяин — барин. Обсуждение, зачем именно это понадобилось — непродуктивно. Но если я правильно вас понял, то вот простой пример решения:
const isProfileUsed = state => ({ ...state, isUsed: true });
const profileParams = combineReducers({
  info,
  credentials,
});

const rootReducer = combineReducers({
  router,
  account: combineReducers({
    profile: reduceReducers(profileParams, isProfileUsed)
    billing,
  }),
  // ... other combineReducers
});

Конечно же isUsed — это просто чистая функция, как и любой редьюсер, и вы можете делать с ней всё, что захотите. Например в привычном виде (у вас в примерах это switch, у меня — createReducer) сделать обработку по нужным actionTypes, а вместо spread-оператора использовать Ramda, Immutable и так далее. Не придумывая дополнительные соглашения по нотации «объекта комбинаций».
Это еще один подход к решению подобной задачи, но вы здесь используете reduce-reducers, то есть не решаете задачу родным API. Поэтому отличие только в реализации. На панацею не претендую, но думаю мой подход кому-нибудь может быть полезен.
Конечно, может быть полезен :) Просто мы обсуждали решение проблемы более «проторенным» путём. Как видите, такой путь есть. Возможно поэтому в опросе подавляющее большинство голосов «нет» и «скорее всего нет»?

Насчёт родного API: redux не даёт такого апи вообще. Всё, что есть — это набор соглашений и полная свобода делать что угодно с корневым редьюсером (кто во что горазд — как было с классическим Flux до появления Redux, который стал стандартом де-факто). CombineReducers был введён лишь чтобы дать похожую на классический Flux возможность разделения общего стейта на отдельные «домены». И в работе с составной логикой в редьюсерах тоже есть стандарт де-факто: обычный функциональный подход.

Btw, у вас могут быть проблемы с мутацией стейта.
Большое спасибо за ревью кода, на самом деле такие проблемы имеют место. В понедельник сделаю фикс.
UFO just landed and posted this here
Почему вы решили, что reduceReducers «раскомбинирует» в плоскую структуру? Это же обычный функциональный fold всех редьюсеров. В моём примере никакого раскомбинирования в плоскую структуру не происходит, это просто slice reducer, который отвечает не за ветки info и credentials, а за весь их родительский узел profile.

А зачем так делать — спросите у автора. Я лишь предложил более простой (по моему скромному мнению) вариант решения проблемы, который проще, чем предложенный автором. Тоже считаю, что где-то тут у автора неправильно сформирована структура данных.
Например, один из уровней несет в себе составную логику, которую тоже было бы неплохо разделить (или как говорил один из известных людей: «Ухлубить!»). Но такого подхода нет в API Redux.

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

Изначально, при знакомстве с редаксом, у меня возникла идея обернуть в combineReducers все ветки, чтобы можно было передавать только схему стора, но попробовав на практике решил использовать наиболее плоскую структуру.
А Вы пробовали на практике эту схему с приложенным кодом? combineReducers — не для этого.
Вложенные структуры оправдывают себя при разведении логики по отдельным файлам, а не писать все редьюсеры кучей в руте. И данный могут хранится в нормализованном виде. Данные никакого отношения не несут на структуру обращения к ним. Я использовал redux со связкой с polymerjs, а сейчас с vue и там я биндю стор через путь, что не несет за собой никакой нагрузки. А вот когда происходит оперирование со сложной иерархической структурой в редьюсере, согласен, что при большом размере структуры могут быть задержки выполнения, но это на себя берет lodash. И это уже дела оптимизации, необязательно же уходить сильно далеко вглубь, и не обязательно хранить большие объемы данных, можно периодически скидывать кэш(в редаксе есть для этого расширения). Но в целом задача по разделению логики решена, причем в достаточно автоматическом режиме: в каждом редьюсере работаете как в руте, и один только раз описывайте комбинации, остальное дело техники.
Документация Redux как раз и предлагает не писать все редьюсеры кучей в руте. Более того, прямым тестом сказано, что combineReducers — просто пример, что каждый может сам себе режиссёр реализовать свой вариант combineReducers. Так что формально ваше предложение не нарушает заветы авторов Redux.

Но для декомпозиции редьюсеров обычно используют функциональный подход, и это оказывается намного удобнее. А для работы с вложенным деревом — нормализацию стейта.
Я ведь не спорю что есть способы добиться того же, например. Но я преследовал цели гибкости и автоматизма и нормализации логики по слоям(разделам) стора.
Добавил сборщик для компиляции проекта
Sign up to leave a comment.

Articles