Structuring Large Redux Applications: Best Practices

Kevin Diesenberg
2 min readJun 22, 2023

As a Redux application grows, managing the complexity and keeping the codebase maintainable can become challenging. In this guide, we will share some best practices for structuring large Redux applications.

Normalizing State Shape

Normalizing your state shape can make it easier to manage your data and reduce redundancy. This involves flattening your state and storing data in lookup tables, similar to how you would structure a relational database.

For example, instead of this:

{
todos: [
{
id: 1,
text: 'Learn Redux',
comments: [
{ id: 1, text: 'Great idea!' },
{ id: 2, text: 'Don't forget to practice.' }
]
},
// More todos...
]
}

You would structure your state like this:

{
todos: {
1: { id: 1, text: 'Learn Redux', comments: [1, 2] },
// More todos...
},
comments: {
1: { id: 1, text: 'Great idea!' },
2: { id: 2, text: 'Don't forget to practice.' }
}
}

Modularizing Reducers

In large Redux applications, your reducer function can grow very large. To avoid this, you can split it into smaller reducer functions, each managing independent parts of the state.

import { combineReducers } from 'redux';
import todos from './todos';
import comments from './comments';

export default combineReducers({
todos,
comments
});

In this example, the todos reducer manages state.todos and the comments reducer manages state.comments.

Using Action Creators

Action creators are functions that create actions. Using action creators can help you keep your code DRY and make it easier to test your action dispatching logic.

export function addTodo(text) {
return {
type: 'ADD_TODO',
payload: { text }
};
}

// Then, dispatch the action like this:
dispatch(addTodo('Learn Redux'));

Using Selector Functions

Selectors are functions that know how to extract specific pieces of information from a store state. Using selectors can prevent code duplication and can make it easier to refactor state shape.

export const getTodos = state => state.todos;
export const getTodoById = (state, id) => state.todos[id];

// Then, use the selectors like this:
const todos = getTodos(state);
const todo = getTodoById(state, 1);

Conclusion

Structuring a large Redux application involves careful planning and organization, but the principles are straightforward. Normalizing state shape, modularizing reducers, using action creators, and selector functions are key best practices that can make your Redux code more manageable, scalable, and maintainable.

In the next post, we will explore integrating Redux with other libraries and tools. Until then, happy coding!

--

--

Kevin Diesenberg

Software Engineer | JavaScript | React | Ruby | Rails | SQL | Skydiver | Outdoor Enthusiast | Paramotor Pilot | Cyclist | Edible Mushroom Hunter