I will use the following popular todo list example for demonstration. Let's say I wanted to create a very basic todo list with this structure as the app state:
{
todos: {
todoKey1: {
content: "todoKey1 content",
...
},
todoKey2: ...
}
}
Because of the recursive nature of reducers, we can separate out the reducers into different files:
// reducers/app.js:
import todos from "./todos";
export default function app(prev={}, action){
return {
todos: todos(prev.todos, action)
};
}
// reducers/todos.js:
import todo from "./todo";
export default function todos(prev={}, action){
switch(action.type){
case "ADD_TODO":
return {
...prev,
[action.id]: todo(undefined, action)
};
default:
return prev;
}
}
// reducers/todo.js:
export default function todo(prev={}, action){
switch(action.type){
case "ADD_TODO":
return {content: action.content};
default:
return prev;
}
}
Everything looks good so far - nice and organized. This is where my question comes in: I don't want to include the new key for the new todo in the action. Instead I want the action to be very pure, in which it should describe only the action (adding a new todo item). The id
is irrelevant.
I would like the key to be automatically generated whenever I add a todo item and thus I have modified the state structure to include next_available_id
which indicates what the key should the next todo item be using:
{
next_available_id: 0,
todos: {
todoKey1: {
content: "todoKey1 content",
...
},
todoKey2: ...
}
}
This breaks the nice recursive reducers that I had since now it todos()
doesn't have the id
to create the new entry and the action does not have the id
. Instead, I will have to combine the reducer in todos.js
into app.js
:
// reducers/app.js:
import todo from "./todo";
export default function app(prev={}, action){
switch(action.type){
case "ADD_TODO":
let todos = prev.todos || {};
return {
...prev,
next_available_id: prev.next_available_id + 1,
todos: {
...todos,
[prev.next_available_id]: todo(undefined, action)
}
};
default:
return prev;
}
}
Possible solutions that I can think of:
- Dispatch multiple actions: one for incrementing
next_available_id
and another one for the actual todo creation.- Pro:
app.js
andtodos.js
can remain separated - Con: This can get really messy in the actual application as there would be a lot of extra actions
- Pro:
- Modify the action itself before passing into
todos
.- Pro: Eliminates the need for an additional action
- Con: Actions should be immutable
I feel like I'm missing something here. What would be the standard way of handling situations like this?