28
My secret trick for writing great React components
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:
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.
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.
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 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.
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>
);
};
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>
);
};
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.
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.
28