Title: Focus ReSwift reducers to one state change ID: 201801041611 Tags: #reswift #srp
ReSwift reducers should not have conditional side-effects, changing different states out of convenience. That makes it hard to notice which action changed what. The condition is most of the problem, I think.
An ideal approach would be to have 1 reducer/action pair for each substate change.
When reducers overlap (they touch the same state), this can become a problem. (IncreaseCounter
and DecreaseCounter
are a bad example.) Conditional changes to another substate should be extracted as another action which is dispatched by a Middleware under similar circumstances.
Bad example with 1 reducer doing 2 things:
public func reduceReloadingResults(
_ action: ReloadingResults,
state: AppState) -> AppState
{
var state = state
state.search.reloadCurrentSearch()
if reloadingDisplayedNoteIsNecessary() {
// Overlaps with `DisplayingNote` action:
state.display = DisplayState(...)
}
return state
}
Better example with a middleware dispatching an already established action:
public func reduceReloadingResults(
_ action: ReloadingResults,
state: AppState) -> AppState
{
var state = state
state.search.reloadCurrentSearch()
return state
}
let refreshDisplayedNoteWhenItTriggeredReloadMiddleware:
Middleware<AppState> = { dispatch, getState in
return { next in
return { action in
guard let reloadAction = action as? ReloadingResults,
// ...
else { next(action); return }
// Continue refreshing search first
next(action)
guard reloadingDisplayedNoteIsNecessary() else { return }
dispatch(DisplayingNote(identifier: identifier))
}
}
}