Reputation: 13914
I have been using Redux for a while, but I still can't see the point of actions and reducers.
As described in the docs, a reducer can be summarised as (previousState, action) => newState
. The same principle applies for React's useReducer
.
So this reducer function basically handles all actions, which seems like a violation of the Single Responsibility Principle. I'm sure that there's a good reason to do it this way, but I don't see it.
It would make more sense to me to just have a function per action. So instead of having an ADD_TODO
action you would have a addTodo(previousState, todoText) => newState
function. This would reduce (no pun intended) a lot of boilerplate code and might even give a slight performance improvement as you no longer need to switch
through the action types.
So my question is: What's the advantage of having a reducer as opposed to a single-action function?
Upvotes: 0
Views: 419
Reputation: 11
Actually it is a silly design and makes no sense.
It is just a kind of over-engineering. Because in most case, you could have just used a simpler function to update a state instead of doing it a silly way by making it two functions (reducer and action).
By adopting redux in your project, you may write tons of reducer and action codes which looks extremely ugly and difficult to do code reviews
Try to imagine, if useState function is design by redux way!
const [todos, setTodos] = useState([])
this one line code now would looks like this(tons of ugly codes):
export interface TODO {
name: string
}
export interface ITodosAction {
type:
| "SET_TODOS";
payload: {
todos?: TODO[];
};
}
export default function todosReducer(
state: TODO[] = [],
action: ITodosAction
): TODO[] {
export function setTodos(todos: TODO[]): ITodosAction {
return {
type: "SET_TODOS",
payload: { todos }
};
}
const todos = useSelector(state=>state.todos)
dispatch(setTodos(["A", "B"]));
if useState became like this, we would have to suffer using react (thanks to react's team for not adopting an over-engineering and silly design like redux).
Remember: never try to figure out why this design is good when it has already brought you trouble. We use redux not because redux is good (instead it is a worst design i've ever seen) but because we don't have many choices
Upvotes: 1
Reputation: 2889
So if your question is why do we use reducers
at all?
The real boilerplate of Flux is conceptual: the need to emit an update, the need to register the Store with a Dispatcher, the need for the Store to be an object (and the complications that arise when you want a universal app).
That is a fundamental design choice redux
has made as it is inspired from flux
.
If you do not like the switch
cases, and there by the reducer size. You can have something like this below:
export const todos = createReducer([], {
[ActionTypes.ADD_TODO]: (state, action) => {
const text = action.text.trim()
return [...state, text]
}
})
Above is a function that lets us express reducers as an object mapping from action types to handlers.
createReducer
can be defined as :
function createReducer(initialState, handlers) {
return function reducer(state = initialState, action) {
if (handlers.hasOwnProperty(action.type)) {
return handlers[action.type](state, action)
} else {
return state
}
}
}
You can read more on this here
Upvotes: 1
Reputation: 55613
I think you're not appreciating how flexible reducers are, you can create as many of them as you like:
const initialState = {
key: 'someValue',
key2: 'someValue',
key3: {
key31: 'someValue',
key32: 'someValue',
key33: []
}
}
// all the below reducers can live in separate files and only handle a single action, if they want, no switches involved at all
const key = (state = initialState.key, action) => { /* the logic to be performed at 'key' */ }
const key2 = (state = initialState.key, action) => { /* the logic to be performed at 'key2' */ }
const key31 = (state = initialState.key3.key31, action) => { /* the logic to be performed at 'key3.key31' */ }
const key32 = (state = initialState.key3.key32, action) => { /* the logic to be performed at 'key3.key32' */ }
const key33 = (state = initialState.key3.key33, action) => { /* the logic to be performed at 'key3.key33' */ }
const key3 = combineReducers({key31,key32,key33})
// this is the top level reducer for your store
const myTopLevelReducer = combineReducers({key,key2,key3});
Upvotes: 0