I have a confession to make. I do not write a single test for my React components. Yes, you read that right, not a single one. You might wonder how I keep track of complex React projects with many many components. Here's my trick:
Always write React components that you can read without scrolling.
As a rule of thumb, if you cannot read a React component without scrolling, then I bet it does more than one thing. It has more than one responsibility, more than one single purpose.
Thinking in React
The first thing you’ll want to do is to draw boxes around every component (and subcomponent) in the mock and give them all names.
This is an actual quote from the React docs that apparently everybody forgot to read.
If you follow this advice every component that you write will do one and only one thing, will serve only one purpose. If it ends up growing, it should be decomposed into smaller subcomponents.
What about complex functions that manipulate data? This is also simple: I just create a pure function with all the logic, save it in a file and just use it in my React components.
Let's see some code
Let's assume I want to add React Context to one of my components.
const AwesomeComponent = (props) => {
const defaults = {
mode: 'dark',
};
const cache = {
mode: local.get('theme.mode'),
};
const initialTheme = merge(defaults, cache);
const [theme, setTheme] = useState(initialTheme);
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<div className="awesome-component">
<div>everything else...</div>
</div>
</ThemeContext.Provider>
);
};
The first part of the component uses the useState
React hook. It also initializes the state with some default values taken from some options cached values.
First improvement
The defaults can really grow over time to many other options, not just mode
. Let's make a function that initializes the state. This function will have a single purpose: initializing the state.
const AwesomeComponent = (props) => {
const [theme, setTheme] = useState(themable());
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<div className="awesome-component">
<div>everything else...</div>
</div>
</ThemeContext.Provider>
);
};
Second improvement
The component still does more than it should. Let's move the hard-to-read React context code in a separate component and just implement it in our main component. This way we will not care how the context is initialized, we will care only that the context WAS initialized.
const AwesomeComponent = (props) => {
return (
<Theme>
<div className="awesome-component">
<div>everything else...</div>
</div>
</Theme>
);
};
More improvements
If you start thinking this way in React you will notice these small changes everywhere in your projects. And your projects will become better and better.
Final thoughts
Code clutter
We're all guilty of it. Junior devs, senior devs, fullstack devs. We all have written God classes in OOP or huge React components without splitting them by purpose.
But this has to change, otherwise the complex project you are working on is going to become a complex monster project.
And it has to change fast. So next time you plan to write some React component, hook or just a plain function: why not split it in multiple files, each one with a single responsibility? The world of programming would be a much better place.
Top comments (19)
"Always write React components that you can read without scrolling."
Tailwind has entered the chat
I usually create old style css name classes and
@apply
directive to get rid of huge inline classes.It also let me split styling from behavioural tailwind classes on different
@apply
directives on the same class somt-2
is not mixed withhover:
and so onWhat's even the point of using tailwind that way and not just use css/sass ?
from the creator himself twitter.com/adamwathan/status/1226...
I am in love with tailwind, the way you can just copy paste html and pass it on to a partner and it should look the same (taking position etc. into consideration).
And never having to scroll trough massive css files is worth the hassle of big HTML.
Well it may be fine but tailwind didn't invent anything nor there is anything innovative into this approcah, that's just old css utility classes, a bit like bootstrap ( although you'd most likely still use custom css with it since it;s opinionated ) and almost verbatim what tachyons basscss etc have been doing for years.
too many class inside single element, make use of tailwind is harder to read.
You are perfectly right, Tailwind did not invent anything new and Tachyons did this years ago. In my personal experience I prefer Tailwind over Tachyons because class names have better names for the first one.
Take
f5-l
which for Tachyons meansfont-size: 1rem
.Tailwind has
font-base
which means the same thing.I also love the fact that I do not have to come up with names for classes. In the old days I would say
container
orcontainer-header
, now I just add utility classes likeflex flex-col gap-2
and everyone in my team understands the CSS behind it.Interesting article. If i understand correctly, Kent would not split a simple component that just contains many many html elements.
I disagree and would split this component right away. The worst case scenario: I have a few extra components included in my main component that are easier to read and have meaningful names.
If I may ask, how large is your largest file on the project you are working on? I am onboarding in a new project where I saw components with over 1000 lines of code. Horror!
I guess you did not see 14000 lines of code :D believe me 1000 is not so bad at all
A 1,000+ line component sounds like a nightmare, I think that is a perfectly valid reason to break it up :)
This is one of the best articles I've seen on this platform. Straight to the problem with easy to understand and simple code examples, good english. Basically everything to keep the reader engaged.
Well done!
Thanks a lot for your feedback, it really made my day better. I'll try to do my best in the future as well. This really is the most important problem that I've noticed in React projects so I'm glad you found it useful.
It's a nice idea, but wading through a million nested components is a debugging nightmare and makes the context of each component difficult to understand for someone who is new to the project.
I always prefer to abstract when I need to rather than adhere to the single responsibility principle. I'm not convinced that universal rules are necessarily always useful.
I was not convinced at first as well, but I found out over and over again that if it's easier to read it has less bugs.
I'm curios about the abstraxtion you were referring. Could you give me an example?
File Organization. React doesn't recommend a specific pattern to organize folder structure, but there are a couple of suggestions on their website. ...
Don't Repeat Yourself. ...
Name Your Components Well. ...
Use PropTypes (or TypeScript) for Type Checking. ...
Use Linting Tools. ...
Separate Display and Business Logic. ...
Write Test Cases.
By: cinemahd-v2.com/
How do you write a component like modal and use for every type of modal.
I use a generic modal component like the modal from the
react-bootstrap
package and I customize it by writing new modals in separate files.Something like this: