I like Redux. It is simple and powerful.
But when I started using it in real word, one architectural question drives me mad.
How to locate my business logic in one place?
Because I have 2 possible places where to locate it:
[AC] -> [Action] -> [SR]
Below is 3 examples.
Ex. 1 and 2 - locate business decisions in AC and SR in sync scenario.
Ex. 3 - business decision made in AC in async scenario.
In my project I've noticed how business decisions are getting scattered between AC and SR very quickly. So each time I want to debug something I should ask myself - ok, so where that decision I want to check is located, AC or SR?
From architectural point of view, I'd rather want to split BL by domains, not by AC/SR.
My point : while I understand advantages of pure reducers that make hot-reloading, time-travel, undo/redo features possible, I'm not sure I'm ready to trade logic maintainability for that.
Still, I have only one week with Redux.
What have I missed?
Example 1 (sync, decisions are in reducer):
// action-creators.js
export function increment() {
return {
type: 'INCREMENT'
}
}
export function decrement() {
return {
type: 'DECREMENT'
}
}
// counter-reducer.js
export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1 // (!) decision of how to '(in|de)crement' is here
case 'DECREMENT':
return state - 1
default:
return state
}
}
Example 2 (sync, decisions are in action creators):
// action-creators.js
export function increment() {
return (dispatch, getState) => {
return {
type: 'CHANGE_COUNTER',
newValue: getState() + 1 // (!) decision of how to '(in|de)crement' is here
}
};
}
export function decrement() {
return (dispatch, getState) => {
return {
type: 'CHANGE_COUNTER',
newValue: getState() - 1
}
};
}
// counter-reducer.js
export default function counter(state = 0, action) {
switch (action.type) {
case 'CHANGE_COUNTER':
return action.newValue
default:
return state
}
}
Example 3 (async, decisions are in action creators):
// action-creators.js
export function login() {
return async (dispatch, getState) => {
let isLoggedIn = await api.getLoginState();
if (!isLoggedIn) { // (!) decision of whether to make second api call or not
let {user, pass} = getState();
await api.login(user, pass)
}
dispatch({
type: 'MOVE_TO_DASHBOARD'
})
};
}
// some-reducer.js
export default function someReducer(state = 0, action) {
switch (action.type) {
case 'MOVE_TO_DASHBOARD':
return {
...state,
screen: 'dashboard'
}
default:
return state
}
}
You've pretty well covered the possibilities. Unfortunately, there is no one-size-fits-all answer. It's your app, you'll have to decide.
The Redux FAQ answer on structuring business logic has a good quote on the topic, and links to a few related discussions.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.