My secret trick for writing great React components
Victor Ocnarescu

Victor Ocnarescu @victorocna

About: 11+ years of experience tangled in the world wide web.

Location:
Europe
Joined:
Sep 29, 2018

My secret trick for writing great React components

Publish Date: Nov 29 '21
203 19

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.

Comments 19 total

  • Bert Meeuws
    Bert MeeuwsNov 30, 2021

    "Always write React components that you can read without scrolling."

    Tailwind has entered the chat

    • mrcbns
      mrcbnsDec 1, 2021

      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 so mt-2 is not mixed with hover: and so on

      • Bert Meeuws
        Bert MeeuwsDec 1, 2021

        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.

        • Heroe
          HeroeDec 3, 2021

          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.

          • muhsarip
            muhsaripDec 3, 2021

            too many class inside single element, make use of tailwind is harder to read.

            • Victor Ocnarescu
              Victor OcnarescuDec 3, 2021

              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 means font-size: 1rem.
              Tailwind has font-base which means the same thing.

              • Victor Ocnarescu
                Victor OcnarescuDec 3, 2021

                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 or container-header, now I just add utility classes like flex flex-col gap-2 and everyone in my team understands the CSS behind it.

      • Heroe
        HeroeDec 3, 2021

        What's even the point of using tailwind that way and not just use css/sass ?

        Confession: The apply feature in Tailwind basically only exists to trick people who are put off by long lists of classes into trying the framework.
        You should almost never use it 😬
        Reuse your utility-littered HTML instead

        from the creator himself twitter.com/adamwathan/status/1226...

  • Ed Perkins
    Ed PerkinsNov 30, 2021

    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.

    • Victor Ocnarescu
      Victor OcnarescuNov 30, 2021

      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?

  • АнонимNov 30, 2021

    [deleted]

    • Victor Ocnarescu
      Victor OcnarescuNov 30, 2021

      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!

      • Joe Attardi
        Joe AttardiNov 30, 2021

        A 1,000+ line component sounds like a nightmare, I think that is a perfectly valid reason to break it up :)

      • l0st0
        l0st0Dec 4, 2021

        I guess you did not see 14000 lines of code :D believe me 1000 is not so bad at all

  • redicrafty
    redicraftyDec 1, 2021

    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!

    • Victor Ocnarescu
      Victor OcnarescuDec 2, 2021

      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.

  • MRJUNNU
    MRJUNNUDec 4, 2021

    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/

  • Sarvesh Dubey
    Sarvesh DubeyFeb 10, 2022

    How do you write a component like modal and use for every type of modal.

    • Victor Ocnarescu
      Victor OcnarescuFeb 15, 2022

      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:

      const ConfirmModal = ({ isOpen, hide }) => {
        return (
          <Modal centered backdrop="static" show={isOpen} onHide={hide}>
            <h2>hello world</h2>
          </Modal>
        );
      };
      
      Enter fullscreen mode Exit fullscreen mode
Add comment