We'll learn how to normalize the state shape to ensure data consistency that's important in real-world applications.
We currently represent the Todos in the state free as an array of Todo object. However, in the real app, we probably has more than a single array and so Todos with the same IDs in different arrays m Ight get out of sync.
Const BYIDS = (state = {}, action) + = { switch (action.type) {case ' add_t ODO ': case ' Toggle_todo ': return { ... state, [ Action.id]: Todo (state[action.id], action), }; default : return State ; }};
For using the object spread, we need to include plugins:
// . Baberc { "presets": ["es2015", "React"], "plugins": ["Transform-object-rest-spread" ]}
Anytime the Byid Reducer receives an action, it's going to return the copy of its mapping between the IDs and the actual t Odos with updated Todo for the current action. I'll let another reducer this keeps track of all the added IDs.
Const ALLIDS = (state = [], action) + = { switch(action.type) {case ' add_ TODO ': return [... state, action.id]; default : return State ; }};
The only action I care about are a todo because if a new Todo is added, I want to return a new array of IDs with that ID as The last item. For any other actions, I just need to return the current state.
Finally, I still need to export the single reducer from the Todos file, so I ' m going to use combined reducers again to COM Bine the Byid and the Allids reducers.
Const TODOS = combinereducers ({ allids, byids});
Export default Todos;
Now, we had changed the state shape in reducers, we also need to update the selectors that rely on it. The state object then get visible Todos are now going to contain Byid and Allids fields, because it corresponds to the stat E of the combined reducer.
Const GETALLTODOS = (state) = = { returnState.allIds.map (id) = { returnState.byids[id]; })};export Const Getvisibletodos= (state, filter) = ={Const Alltodos=Getalltodos (state); Console.log (Alltodos); Switch(filter) { Case' All ': returnAlltodos; Case' Completed ': returnAlltodos.filter (t =t.completed); Case' Active ': returnAlltodos.filter (t =!)t.completed); default: Throw NewError (' Unknown filter: ${filter}. '); }};
My todos file has grown quite a bit so it's a good time to extract the TODO reducer this manages just when you go todo int o A separate file of its own. I created a file called Todo in the same folder and I'll paste my implementation right there so that I can import it fro M the Todos file.
Reducers/todo.js
Const TODO = (state, action) = = { Switch(action.type) { Case' Add_todo ': return{id:action.id, text:action.text, completed:false, }; Case' Toggle_todo ': if(State.id!==action.id) {returnState ; } return{... state, completed:!state.completed,}; default: returnState ; }};
------------------
//Todos.jsImport {combinereducers} from' Redux '; import Todo from'./todo '; Const Byids= (state = {}, action) = = { Switch(action.type) { Case' Add_todo ': Case' Toggle_todo ': return{... state, [action.id]: Todo (state[action.id], action),}; default: returnState ; }};const Allids= (state = [], action) = = { Switch(action.type) { Case' Add_todo ': return[... state, action.id]; default: returnState ; }};const Todos=combinereducers ({allids, byids}); exportdefaultTodos;const Getalltodos= (state) = = { returnState.allIds.map (id) = { returnState.byids[id]; })};export Const Getvisibletodos= (state, filter) = ={Const Alltodos=Getalltodos (state); Console.log (Alltodos); Switch(filter) { Case' All ': returnAlltodos; Case' Completed ': returnAlltodos.filter (t =t.completed); Case' Active ': returnAlltodos.filter (t =!)t.completed); default: Throw NewError (' Unknown filter: ${filter}. '); }};
//Todo.jsConst TODO = (state, action) = = { Switch(action.type) { Case' Add_todo ': return{id:action.id, text:action.text, completed:false, }; Case' Toggle_todo ': if(State.id!==action.id) {returnState ; } return{... state, completed:!state.completed,}; default: returnState ; }};exportdefaultTodo
[Redux] Normalizing the state Shape