Exactly how to structure your React / Redux applications is a contentious point, a quick Google on the subject will bring up a myriad of recommendations and methods so I thought, why not add to the pile and throw my own recommendation into the ring?
React and Redux themselves are both pretty unopinionated on application architecture, Dan Abramov himself had this to say on the subject. Whilst I entirely agree that what works for you is generally the best approach to problems like this, I also believe that employing a consistent approach within organisations is important – it allows our developers to move between projects more easily with much shorter ramp up times, bug fixing tends to be a lot easier and making improvements on practice and process is a lot easier when those practices and processes are consistent in the first place.
In the spirit of this, the purpose of this post isn’t to tell you how you should be doing it, it’s about how we, at Amido, like to structure our own React projects. It’s an architecture we’ve settled on (for now) after a lot of trial and error, it’s based on the popular Ducks methodology, a methodology which promotes grouping by feature instead of function and it works pretty well for us – allowing us to scale our applications and on-board new team members more efficiently.
The top level of our applications, within the ‘src’ directory is comprised of 3 levels, and I’ll delve into each of these separately:
Here we keep our reusable components, they are unopinionated and can manage their own state, or be stateless. They take props and render accordingly and nothing else – any logic contained in these components should be display logic only.
These are collections of components that make up a module, this could be an entire view in an application, for example a page in React Router managed app or a reusable module that appears in multiple views. Each Composition is comprised of a React component and a Redux connector, like so:
—— └ HomePage
———— └ index.js
———— └ connect.js
Keeping our component and connector separate means each can be tested in isolation. We don’t need to test whether or not Redux is working correctly, simply whether our connector is outputting the correct props and our components are displaying correctly when receiving props. So our component will look kind of like this: