Comments 21
Если честно, то непростой задачей представляется масштабирование примера приложения с одной чистой функцией в качестве бизнес логики до полноценного приложения в духе DDD.
Как такое делать в 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))
}
})
})
Можно попробовать — тынц
Кстати, до меня дошло, что это просто функция redux-thunk, если немного подправить:
const appLoad = config => dispatch =>
// ...
Компоненты высшего порядка (HOC) как вариант.
В случае чистой синхронной функции — selector это что-то само собой разумеющееся. Это пока далеко от реальной жизни...
В случае хитрой бизнес логики с асинхронными запросами лучше всего таки идти через action creator. Да хотя бы методом исключения: редьюсер синхронный, селектор синхронный, lifecycle-методы синхронные и вообще мы их не рассматриваем, остаются только action creator'ы, ну может middleware, но зачем?
Принцип следующий:
- Диспатчим то, что пришло из формы синхронно
- Сразу в этом же action creator получаем новый полный стейт, по которому уже прошли редьюсеры (в стейте надо поставить какой-то флаг, что он недокалькулирован)
- Вырезаем от стейта часть, нужную для запроса на сервер (самый минимум) и засылаем
- Диспатчим результат работы сервера новым экшном
- По прошлой схеме набрасываемся на стейт редьюсерами (флаг можно снять) и селекторами (которые могут добить клиентские простые вычисления)
Вся эта бодяга либо уже есть в виде библиотеки (кто-то точно до этого додумался) либо может быть описана в виде некой функции-фабрики action-creator'ов.
Единственный минус — логика размазывается между селекторами и редьюсерами, но если четко обозначит сферы ответственности (редьюсер — операции закладки данных в стор, селектор — операции над данными без записи), то это не проблема.
В модели (это стор, то есть редьюсеры). А вызываться — из контроллера. Контроллер в редаксе — это набор 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 засунуть не удастся, по-этому такой вариант и обсуждать особого смысла не имеет. Ну а что до тривиальной — понятно же, что все подобные утверждения вроде «так делать нельзя» не имеют статуса максимы и по факту обозначают что-то вроде «нельзя без явных причин». Если у вас есть явная причина засунуть что-то в селектор (вы четко видите, что это проще и удобнее других решений при прочих равных) — ну ради бога. Главное, чтобы задача была качественно решена.
Абрамов в одном из роликов по редаксу демонстрировал, как переместить селекторы к редюсерам. Очевидно, с целью сконцентрировать бизнес-логику в одном месте.
Redux Business Logic