Pull to refresh

Comments 21

Если честно, то непростой задачей представляется масштабирование примера приложения с одной чистой функцией в качестве бизнес логики до полноценного приложения в духе DDD.

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

Мне вот куда интереснее как в redux делать повторно используемую логику. Скажем есть 5 master-detail страниц в приложении. На каждой — фильра с редактировнием и сохранением в URL, ленивая подгрузка данных, редактирование — с подгрузкой данных, валидацией, сохранением. Но в каждой, понятно, разные кнопочки с действиями, разный набор фильтров, колонок.

Как такое делать в redux?

Пример выноса общего кода на redux-thunk:


export const appLoad = (dispatch, config) =>
  new Promise((resolve, reject) => {
    let isTimeout = false
    let isFetch = false
    setTimeout(() => {
      isTimeout = true
      if (isFetch) {
        dispatch(setLoading(false))
      }
    }, 500) // демонстрировать state.app.isLoading не менее 500 мс
    axios(config)
      .then(response => {
        resolve(response.data)
      })
      .catch(error => {
        dispatch(setMainError(error.toString()))
        reject(error)
      })
      .then(() => {
        isFetch = true
        if (isTimeout) {
          dispatch(setLoading(false))
        }
      })
  })

Можно попробовать — тынц

А вот тут: https://github.com/comerc/yobr/blob/master/src/ducks/postForm.js — что делать, если форм будет штук 10 — у всех разные правила валидации, разные API для загрузки/сохранения, но в целом логика одинаковая?

Кстати, до меня дошло, что это просто функция redux-thunk, если немного подправить:


const appLoad = config => dispatch => 
  // ...

Компоненты высшего порядка (HOC) как вариант.

В случае чистой синхронной функции — selector это что-то само собой разумеющееся. Это пока далеко от реальной жизни...


В случае хитрой бизнес логики с асинхронными запросами лучше всего таки идти через action creator. Да хотя бы методом исключения: редьюсер синхронный, селектор синхронный, lifecycle-методы синхронные и вообще мы их не рассматриваем, остаются только action creator'ы, ну может middleware, но зачем?


Принцип следующий:


  1. Диспатчим то, что пришло из формы синхронно
  2. Сразу в этом же action creator получаем новый полный стейт, по которому уже прошли редьюсеры (в стейте надо поставить какой-то флаг, что он недокалькулирован)
  3. Вырезаем от стейта часть, нужную для запроса на сервер (самый минимум) и засылаем
  4. Диспатчим результат работы сервера новым экшном
  5. По прошлой схеме набрасываемся на стейт редьюсерами (флаг можно снять) и селекторами (которые могут добить клиентские простые вычисления)

Вся эта бодяга либо уже есть в виде библиотеки (кто-то точно до этого додумался) либо может быть описана в виде некой функции-фабрики action-creator'ов.


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

lifecycle-методы синхронные и вообще мы их не рассматриваем

Тут можно поспорить, я уже такого нагородил в componentDidMount :)


И в Next.JS у компонентов есть асинхронный getInitialProps.

Я в курсе, но для примера сознательно это не рассматривал.

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

в redux-act есть такая удобная штука — batch

> «где в React-приложении должен быть расположен код, отвечающий за бизнес-логику».

В модели (это стор, то есть редьюсеры). А вызываться — из контроллера. Контроллер в редаксе — это набор action creators, то есть больше бизнес-логику вызывать просто неоткуда, других точек входа не существует. А селекторы — это view, в них бизнес-логику нельзя располагать ни в коем случае, только логику вида. Иначе unidirectional data flow оказывается нарушен.

> Взглянем на action creator подробнее. Сначала ответим себе на вопрос: «у нас будет один action creator на все поля формы, или по отдельной функции на каждое из полей».

Один action сreator, который принимает a,b,c,d и возвращает type: «UPDATE_RESULT», payload: f(a,b,c,d). Сами a,b,c,d в сторе хранить не надо, т.к. они не входят в модель. Только результат.
Сами a,b,c,d в сторе хранить не надо, т.к. они не входят в модель

Я пока храню всё в сторе, игнорируя существование локального стейта компонентов, тупо для упрощения разработки.

> Я пока храню всё в сторе, игнорируя существование локального стейта компонентов

С-но вы же жаловались на то, что стор слишком толстый — вот я его и сделал еще тоньше :)

Мопед не мой, статья — конспект подкаста ради фидбека. Я же иду по стопам Твиттера, они сначала все хранили в сторе редакса, и потом только в рамках оптимизации вынесли состояния в локальный стейт компонентов, там где это потребовалось.

> они сначала все хранили в сторе редакса

Но вы же значение там хранить не хотите. У вас очень странным образом нежелание хранить в сторе модель (то есть то, что там хранить нужно) соседствует с желанием хранить состояние интерфейса (то, что как раз не обязательно).

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

Но, конечно, если известно, что приложение не разрастется и не будет предоставлять каких-то сложных взаимодействий — такой подход тоже вполне приемлем.
А селекторы — это view, в них бизнес-логику нельзя располагать ни в коем случае, только логику вида. Иначе unidirectional data flow оказывается нарушен.

Если, например, стор характеризуется каким-то сложно вычисляемым статусом и его надо и показать, и совершать какие-то бизнес-действия при достижении определенных значений, то неужели надо дублировать логику вычисления? Redux вообще ничего не знает о view по сути. Это всякие redux-react коннекторы связывают стор с вью.

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

А вы можете в селекторах совершать какие-то действия? Наверное, нет.
Нетривиальную логику в селекторы просто by design засунуть не удастся, по-этому такой вариант и обсуждать особого смысла не имеет. Ну а что до тривиальной — понятно же, что все подобные утверждения вроде «так делать нельзя» не имеют статуса максимы и по факту обозначают что-то вроде «нельзя без явных причин». Если у вас есть явная причина засунуть что-то в селектор (вы четко видите, что это проще и удобнее других решений при прочих равных) — ну ради бога. Главное, чтобы задача была качественно решена.

Абрамов в одном из роликов по редаксу демонстрировал, как переместить селекторы к редюсерам. Очевидно, с целью сконцентрировать бизнес-логику в одном месте.

Можно линк на это видео?

Sign up to leave a comment.

Articles