I'm not sure how much this matters in the era where generative AI writes code for you. However, still I embark upon this journey hoping this will help someone write better react code.
Why you should organize your react components
React itselft is unopinonated(probably one of the key reasons why people love react so much) about how component should be organised, and how directories should be placed.
However, organising components in a consistent pattern is necessery, because of the following reasons
Ease of navigating through code
Maintaining Hiearchy helps visualize resulting dom structure
Avoiding Spaghetty Code
Seperating reusable and non reusable components
Ease of composition of complex components
Separation of render-only components from components that contains business logic, or side effects. This is also known as Dumb and Smart Component
Makes Application Preditable.
So, How do I organize my react code?
I follow few levels of seperation to organize my code.
Level 0: All components should have their own directory.
Components should not be exported directly and they should be exported through an index
file
.
└── your-component/
├── index.ts
└── Component.tsx
Level 1: Container and Components Pattern
Also known as Dumb and Smart Components, and sometimes known as View/Presentation Components and Smart Components. Let's Elaborate.
Containers(or smart components) are components that handles side effects, often implements business logic and may change state of the applications. These are the components that
implements useEffect for interacting with APIs,
Usually doesn't have a prop.
changes state of the application, by dispatching actions to context api stores or redux state managers.
Does not implement any html markup, just renders dumb components
have no css/scss styles associated
Are the dynamic parts in your application
Components(or dumb components) are the components that does not change applications state and does not handle side effects.
They are static components, just rendering/reacting to the props that are passed to it.
They may implement useState to manage internal state
May implement useEffect without any side effects, like for reacting to window resize, or to determine if clicked outside.
totally dependent on props, and acts like pure functions, ie. always returns same markup, if the props does not change. hence, its easy to memoize these components.
Usually implements html markup with css/scss styles.
side effects: when you interact and change anything outside the scope of your component, its called a side effect. for eg api calls, change in application state etc
Level 2: Atomic Pattern
No matter what people say, there has been no better pattern of organizing your react code. Read more here about it. In this pattern, you divide your code into atoms, molecules, organisms, templates and pages.
Atoms and Molecules are always Dumb Components. They are simple elements like inputs and buttons, paragraphs and images, or a little complex ones, like Hero elements with images, headers with navs and icons, footer with links.
Organisms are the intelligent ones, so they are Smart Components or Containers. These are the components that make your Single Page Applications dynamic.
Templates are often dumb components, and they give the template for the whole page, like the position of static elements like header, footer, sideNav and position of dynamic components like organisms. Usually they are the layout components.
Pages are highest level, they actually provide the real content to components.
Level 3: Directory Structure
.
└── your-react-app/
├── App.tsx
├── index.tsx
└── src/
├── containers/
│ └── organisms/
│ ├── product
│ ├── cart
│ ├── home
│ └── user
└── components/
├── atoms/
│ ├── buttons
│ ├── images
│ └── paragraph
├── molecules/
│ ├── card
│ ├── hero
│ └── header
└── templates/
├── home-layout
├── cart-layout
└── product-layout
Rules of orchetrating these components
A Dumb Component can contain other dumb components, usually while creating molecules
A Template Component usually implements the render props pattern, and the children that it renders is usually a smart component or organism
return (<div>
<Header />
<div>
{children}
<div>
<Footer />
</div>);
Smart Components should never implement html markup, instead they should return Dumb Components, which in turn implements the markup
Its better to nest a smart component inside a dumb component instead of prop drilling
Each Component, smart or dumb, should contain its own unit test
Conclusion
I acknowledge that perfecting these patterns takes some maturity and some thought. and it took a while for me to build up the muscle memory for this.
For practical purposes, its better to use a component generator. I have used quite a few component generators, however, over the years they all, one by one started to become inadequeate.
Therefore I created my own component generator that is capable of generating react components that implements both component/container pattern and atomic pattern, with lots of options to customize.
https://www.npmjs.com/package/@jstb/catalyst
You may take a look. Its still evolving, so if you want a feature or discover an issue, feel free to raise an issue in the github.