React Component Hierarchy: Lessons Learned

At Venga we’ve been using React in production for over a year. It’s a solid choice when we need to get things done quickly and we want those things to be reasonably structured and maintainable. Along the way we’ve made mistakes and we continue to learn, but we’ve identified some practices that make working with React more pleasant. I figured I’d share some of those lessons with you. They include:

  • Manage your app through a single parent component
  • Keep track of all state in the parent
  • Pass state to subcomponents through props

Let’s develop a simple project that highlights these practices.

The Project

Sales wants to provide clients with a “Top Guests” reporting tool. After some back and forth we settle on the following mockup:

On the dev side we determine that the tool should have the following properties:

  • All 3 select fields are required
  • We should not allow submission until all requirements are met
  • At any point, we should be able to reset this form back to an initial state
  • Once successfully submitted our tool should display a notification and reset back to an initial state

Because we’re good students of React (and we’ve read Thinking in React), we take a crack at highlighting and naming potential React components:

  • Application (Red): Our parent component
  • Feedback (Pink): Will receive feedback data
  • Select (Blue): Each select will have its own component
  • Submit (Green), Reset (Green)

The End Result

Below you’ll find the codepen to the final product. Use this code to follow along as I discuss lessons learned.

<p data-height=”465″ data-theme-id=”dark” data-slug-hash=”gwVZOK” data-default-tab=”js,result” data-user=”leveille” data-embed-version=”2″ data-pen-title=”React Component Hierarchy” class=”codepen”>See the Pen <a href=””>React Component Hierarchy</a> by Jason Leveille (<a href=””>@leveille</a>) on <a href=””>CodePen</a>.</p>
<script async src=””></script>

Manage your app through a single parent component

Take a look at the following example:

<p data-height=”265″ data-theme-id=”dark” data-slug-hash=”KNKxmJ” data-default-tab=”js,result” data-user=”leveille” data-embed-version=”2″ data-pen-title=”Single Parent Component” class=”codepen”>See the Pen <a href=””>Single Parent Component</a> by Jason Leveille (<a href=””>@leveille</a>) on <a href=””>CodePen</a>.</p>
<script async src=””></script>

If you’re ever tempted to render 2 different components, and those components are likely going to want to share state, don’t do it. You end up having to overcomplicate things in order to share state between the two. Instead, simply mount those components as children of the same parent.

In our example app, you can see we take that approach. We have a single root component that bootstraps our application.

Keep track of state in the parent

I’ve found that keeping track of state as close to the single root component as possible makes for easier development. Keeping that state high up in the component tree and passing it along to child components as props has various advantages. First, it tends to keep those child components dumb. Child components should know as little as possible, and they should only know it when they need to know it. Second, it moves the data model to a single location. Trying to track bits and pieces of data across multiple components overcomplicates things. Instead, I like to update state in a single place (typically the parent), and re-render components when their props change.

Take a look at the following example:

<p data-height=”265″ data-theme-id=”dark” data-slug-hash=”mOdGoK” data-default-tab=”js,result” data-user=”leveille” data-embed-version=”2″ data-pen-title=”State In the Parent” class=”codepen”>See the Pen <a href=””>State In the Parent</a> by Jason Leveille (<a href=””>@leveille</a>) on <a href=””>CodePen</a>.</p>
<script async src=””></script>

One example centralizes state in the parent, while the other moves state tracking into the select component. They both accomplish the same thing (allowing you to reset to an original state) and on the surface may not look all that different. However, as you add more features (have more components to “reset” for example), you’ll find the “Dont” approach will become unwieldy. At first glance, passing functions as properties to child components may feel strange. Doing that as a mechanism for you to control state in the parent may also feel strange. You’ll be surprised at how far this technique can get you though.

As you can see in our report example, the only place where we are using setState is in the main Application component. That’s not to say that you should never track state in a child component. There will be times when it will feel appropriate to do so. However, the majority of the time we find it best to avoid doing so.

What about Flux / Reflux?

Centralize your state in the parent and update that state through function props. See how far that gets you. If your app gets large and you’re having to pass functions through 3 levels of child components or more, than you might want to consider a state management tool like Reflux. Otherwise, don’t worry about it. You probably don’t need it.

Some things to Note

Obviously there’s more going on in the core example than I’ve covered in this post. For example, trace through to see if you can figure out:

  • How we control whether or not the submit button is disabled
  • How we control the text color of our required select fields