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_idand another one for the actual todo creation.- Pro:
app.jsandtodos.jscan 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?