How to create a Redux reducer by convention

Redux is a very popular state management library. It simplifies the original Flux architecture by combining all stores and the dispatcher in a single store object.
Redux promotes the use of functional programming for managing state. It introduces the reducer function concept.
Data flow
Let’s look at the data flow inside the Redux store.

An action is a plain object that contains all information necessary to do that action.
An action creator is a function that creates an action object.
Reducer
A reducer is a pure function that takes state and action as parameters and returns the new state.
There may be many reducers managing parts of the root state. We can combine them together with combineReducers() utility function and create the root reducer.
Here is how the todos reducer may look like:
import matchesProperty from "lodash/matchesProperty";function todos(todos = [], action) { switch (action.type) { case "add_todo": const id = getMaxId(todos) + 1; const newTodo = { ...action.todo, id }; return todos.concat([newTodo]); case "remove_todo": const index = todos.findIndex(matchesProperty("id", action.todo.id)); return [...todos.slice(0, index), ...todos.slice(index + 1)]; case "reset_todos": return action.todos; default: return state; }}export default todos;The state in this case is the list of to-dos. We can apply to its actions like add_todo, remove_todo, reset_todos.
Reducer by convention
I would like to get rid of the switch statement in the reducer. Functions should be small and do one thing.
Let’s split the reducer into small pure functions with names matching the action types. I will call these setter functions. Each of them takes state and action as parameters and returns the new state.
function remove_todo(todos, action) { const index = todos.findIndex(matchesProperty("id", action.todo.id)); return [...todos.slice(0, index), ...todos.slice(index + 1)];}function reset_todos(todos, action) { return action.todos;}function add_todo(todos, action) { const id = getMaxId(todos) + 1; const newTodo = { ...action.todo, id}; return todos.concat([newTodo]);}redux-actions
I would like to combine all these small functions together to create the original reducer function. We can use the handleActions() utility function from redux-actions for this.
import { handleActions } from "redux-actions";const reducer = handleActions( { remove_todo, reset_todos, add_todo }, []);export default reducer;The setter functions will run by convention. When an action with type remove_todo comes in, the remove_todo() setter function will be executed.
redux-points
At this point, I would like to create action types based on the setter functions’ names. Here is how the object with all action types may look like:
{ remove_todo : "remove_todo", reset_todos: "reset_todos", add_todo: "add_todo"}I would like to use a utility function to create both the reducer and the action types. Here is how the code may look like:
const { reducer, actionTypes } = createReducerAndActionTypes( { remove_todo, reset_todos, add_todo }, []);export default reducer;export { actionTypes };redux-points is a micro npm library created for this purpose. Here are the functions available:
createReducer(): creates the reducer by taking in an object with all setter functions and the initial state.
createActionTypes(): creates the action types by taking in an object with all setter functions.
createReducerAndActionTypes(): creates both the reducer and the action types by convention. It takes in all setter functions and the initial state.
Once created, the reducers and all available action types can be exported. Let’s use the action types in action creators.
import { actionTypes } from "../store/todos";const { reset_todos, remove_todo, add_todo } = actionTypes;function resetTodos(todos) { return { type: reset_todos, todos };}function addTodo(todo) { return { type: add_todo, todo };}function removeTodo(todo) { return { type: remove_todo, todo };}export { resetTodos, addTodo, removeTodo };Conclusion
A reducer is a pure function that takes state and action and returns the new state.
The reducer function can be created by convention. redux-actions can be used for this purpose.
Try the redux-points npm package to split the reducer into small pure functions with names matching the action types. Combine the small pure functions to create the original reducer and the associated action types.
Here is the sample code on codesandbox.
For more on Redux and Functional Programming JavaScript take a look at:
How to learn Redux from a functional programming perspective
An introduction to the Flux architectural pattern
Learn pure functions with JavaScript
Learn immutability with JavaScript
Discover Functional Programming in JavaScript with this thorough introduction
How point-free composition will make you a better functional programmer
