My secret trick for writing great React components

victorocna

Victor Ocnarescu

Posted on November 29, 2021

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:

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.

Thinking in React

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>
  );
};
Enter fullscreen mode Exit fullscreen mode

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>
  );
};
Enter fullscreen mode Exit fullscreen mode

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>
  );
};
Enter fullscreen mode Exit fullscreen mode

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.

💖 💪 🙅 🚩
victorocna
Victor Ocnarescu

Posted on November 29, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related