As the scope, size, and feature set of your React + Redux application grows, so does the pain of maintaining it, especially when you’re working with a big team, and multiple people work on each feature in your application. In this article, we are going to be exploring how to organize your codebase, to increase maintainability and extensibility. When you first start writing a new React application, the feature set is small, and the React API is simple, yet flexible and powerful enough to handle state management. Eventually however, your application grows bigger, and the shortcomings of React’s at handling state management for larger applications become obvious, in a painful manner. setState So you do a bit of research and figure out is your best bet at handling the growing state management needs of your application. Redux introduces , , , and . And since this is a React application, you'll need to wire Redux to your React components, so you'll also want to use . Add all these up, and your application can quickly become a huge mess of spaghetti code, unless you organize your application properly. Redux actions actionCreators reducers middleware stores React-Redux Ensuring that your application is organized properly, in a manner that is easy to understand for your entire team, can save you a lot of time, and resources while maintaining and extending the application. I have been writing React applications for the past three+ years, with these applications ranging in complexity from trivial to ultra-complex. Over the course of developing these applications, I have experimented with multiple ways of organizing my React applications, and I think I’ve finally settled on the pattern that provides the best balance of ease-of-use and maintainability. I call this pattern of organizing my application, the pattern. But before, we look at the pattern, let's first look at the other patterns that I went through, and why I found them lacking. State-View State-View Grouping by Function/Type of File In a React + Redux application, we can have multiple different types of files, each of which perform a specific function. For example, we have which derive a new state from the given state and an , which create the to pass on to the , the which holds the state of the application, and allows to subscribe to changes to the store, the which create the views that are rendered on the screen, the which do the bulk of the heavy lifting and provide the business logic, persistence etc. reducers action actionCreators action reducers store components presentational components container components The most common way to organize a React + Redux application, is to simply group your files by the type/function of that file. This is how a vast majority of React + Redux applications are organized. If you look at the , from the Redux Github repository, you’ll see that this is how it is organized. Real-World example In the real-world example project: , are two (only one of which is used based on the running environment of the application), and these are grouped under the directory. configureStore.dev.js configureStore.prod.js stores store and both export multiple reducers, and these are grouped together under the directory. index.js paginate.js reducers provides a Redux middleware, which is used to make API calls to Github's API, and normalize the results. This is located under the directory. api.js middleware The various are grouped together under the directory. container components containers The various are grouped together under the directory. presentational components components The actions are all defined in a single which is placed under the directory. index.js actions The entry point of the application is placed directly at the root. index.js The advantage of organizing your application by grouping files with the same function together, is that you always know exactly where a specific type of file is going to be. For example: If you are debugging an issue with state transitions not happening correctly, you know that the only place where a state transition can happen is within a , and all are located in the directory. reducer reducers reducers This works well as long as your application is small and has only a few components and associated , and . As your application becomes bigger, you will find yourself dumping more and more files which are only tangentially related to each other in the same directory, and going through these files to isolate specific issues will start consuming more of your time and resources. This is only exacerbated, when multiple developers are working on the same codebase, and are all touching the same files in the same directory to get their own individual features working. actions reducers middleware Grouping by Application Feature The main problem with the ‘Function/Type based grouping’ approach to organizing your codebase, is that to work on a single feature, you will have to work on files which are distributed across your entire project, in multiple unrelated directories. In order to alleviate this problem, you can instead organize your React + Redux application by grouping files related to a specific feature of your application together. Modifying the same Redux Real-World example to group by application feature would make it look like this: Here we have split up our code into different ‘features’: - The root container component which performs routing and renders other components. Since this does not maintain or manage state, it does not have any or in the directory. We have also renamed the container component and suffixed the file with to indicate it as a component. Root actions reducers Container container - The main application component. Since this does maintain a state to indicate whether or not the API call errored out, it has and in the directory. You may notice we have renamed the component to . This is to indicate this as a presentational component. App actions.js reducers.js Explore ExploreComponent - Is responsible for the DevTools which log the state as it transitions with each action. Does not manage any state of it's own, so this directory only has the components. DevTools and - These directories correspond to the feature which lists the starred watchers of a repo, and a feature which lists the repos a user has. These components manage their own state, specifically they manage the and properties of the application state object. Remember, this as it will become important shortly. RepoPage UserPage RepoPage UserPage entities pagination - Any , , or that is shared across multiple features goes here. common component middleware reducer action This is already looking so much better than ‘Function/Type based grouping’. If we want to work on a particular feature, all the files related to that feature are in the same directory. This includes the , , and . Both view and state related code is sitting in the same directory, thus making it quite easy for us to make changes for a specific feature, without having to go and modify five different files in five different directories. actions reducers presentational components container components This approach usually works well for those applications where there is a one-to-one mapping between state items and view. For example, in a todo list, each todo item is only ever displayed in one location on the screen, via a single presentational component. Thus we are able to segregate the state of the application cleanly based on the feature that makes use of that state, and we can group the related and together with the view components for that feature. actions reducers But what happens when the mapping between state items and view components is not one-to-one. This is exactly what happens in the case of the ‘real-world redux example’ application we are analyzing. In this application, both and functionality, independently make use of the and properties on the state object. Both the features read and write to these properties, by sharing the same and between them. RepoPage UserPage entities pagination actions reducers In such a case how do we group the state and the view together? Which and should be grouped with and which should be grouped with ? Grouping the state related code, mainly the and with either one feature, breaks our grouping model, and it is no longer true that we always have all the code related to a specific functionality in a single directory. Putting the and in also results in the exact same issue. actions reducers RepoPage UserPage actions reducers actions reducers common The State-View Pattern Recently, I’ve been working on a pretty complex React + Redux application, for which I experimented with a new pattern of organizing my application’s codebase. This is an evolution of the ‘feature-based grouping’ approach to organizing your code. Pretty much the only issue (albeit a major one), of the feature-based grouping approach, is unpredictability with regards to state, when the state does not map one-to-one with views. In order to eliminate this unpredictability, we split the codebase into two seperate main directories, and files are grouped based on different factors in each directory. As you may have guessed, these two directories are the and the directories. state view All state management related code, including all , , and are put into the directory. All other code is put into the directory. So how are these directories further organized? actions reducers middleware stores state view Let’s first look at how the overall application structure looks when the real-world Redux example is rewritten to use the pattern: State-View There are the two main directories and . Let's explore the directory first. state view view - The root container component which performs routing and renders other components. All state management related code is removed from here, and only view specific code is retained. We have also renamed the container component and suffixed the file with to indicate it as a component. Root Container container - The main application component. You may notice we have renamed the component to . This is to indicate this as a presentational component. App Explore ExploreComponent - Is responsible for the DevTools which log the state as it transitions with each action. DevTools and - These directories correspond to the feature which lists the starred watchers of a repo, and a feature which lists the repos a user has. As you can see, these directories only contain the view components and all and have been removed. RepoPage UserPage RepoPage UserPage actions reducers - All the view components which are shared between multiple features, are grouped together here. _shared As you can see the directory is organized extremely similarly to how the application is organized in the feature-based grouping pattern. The reason for this is pretty simple. There was no real issue with the way the components were organized in the feature-based grouping pattern, so we just left it as is. view view Now let’s look at the directory. This is where the majority of the changes from the feature-based grouping pattern occur. First off, you will notice that the names of the subdirectories inside the directory do not at all correspond with those of the subdirectories inside the directory. This is completely intentional, and is done to enforce the idea that the state does not have a one-to-one mapping with the view. state view state Instead of organizing the directories contents by application feature, we organize them by the properties of the store that they are responsible for. In the real-world redux example, the state of the application looks something like this: state const state = { errorMessage: null, entities: {}, pagination: {}}; If we go back to the directory, the subdirectories correspond to the properties of the state. Each subdirectory contains the and required for manipulating that particular portion of the state. Any commonly shared items, are placed in the directory. These and are then rolled up and combined, by using the and files in the directory, and they are then exported by the . So now, if we need to use a specific action in any of our view components, we can require it like this: state actions reducers _shared actions reducers actions.js reducers.js state index.js import { actions } from '../../state'; const { loadRepo, loadStargazers } = actions; If you want to have a look at the code for the pattern for the real-world redux example application, the source for that is available on . state-view Github Thus we have effectively decoupled the way our state related code is organized from the way our view related code is organized. Since state is now no longer grouped together with views, we again have a very predictable pattern for our code. Any view related code will always be in the corresponding feature subdirectory in the directory. Any state manipulation performed by those views will be in the subdirectory with the corresponding state property name in the directory. Thus so long as you know where your view is (which should be easy to figure out based on the feature it is part of), you can easily figure out what state it impacts and where the corresponding state manipulation code resides. view state Hoorah! We have restored predictability to our React + Redux application’s structure, and this should enable us to maintain and extend our application, a lot easier than we could have done it before. I’d love to hear your opinion on things I’ve written in this article. So do chime in with your comments below. Originally published at asleepysamurai.com on January 11, 2019.