This site runs best with JavaScript enabled.

Prop Drilling

May 21, 2018


What it is, why it's good, why it's bad, and how to avoid common problems with it

Current Available Translations:

The goal of this post is to not only help you understand what prop drilling is (some also refer to it as "threading"), but also when it can be a problem and mechanisms you can use to side-step or avoid it.

What is prop drilling?

Prop drilling (also called "threading") refers to the process you have to go through to get data to parts of the React Component tree. Let's look at a very simple example of a stateful component (yes, it's my favorite component example):

1function Toggle() {
2 const [on, setOn] = React.useState(false)
3 const toggle = () => setOn(o => !o)
4 return (
5 <div>
6 <div>The button is {on ? 'on' : 'off'}</div>
7 <button onClick={toggle}>Toggle</button>
8 </div>
9 )
10}

Let's refactor this into two components now:

1function Toggle() {
2 const [on, setOn] = React.useState(false)
3 const toggle = () => setOn(o => !o)
4 return <Switch on={on} onToggle={toggle} />
5}
6
7function Switch({on, onToggle}) {
8 return (
9 <div>
10 <div>The button is {on ? 'on' : 'off'}</div>
11 <button onClick={onToggle}>Toggle</button>
12 </div>
13 )
14}

Simple enough, the Switch needs a reference to the toggle and on state, so we're sending some props there. Let's refactor it once more to add another layer in our component tree:

1function Toggle() {
2 const [on, setOn] = React.useState(false)
3 const toggle = () => setOn(o => !o)
4 return <Switch on={on} onToggle={toggle} />
5}
6
7function Switch({on, onToggle}) {
8 return (
9 <div>
10 <SwitchMessage on={on} />
11 <SwitchButton onToggle={onToggle} />
12 </div>
13 )
14}
15
16function SwitchMessage({on}) {
17 return <div>The button is {on ? 'on' : 'off'}</div>
18}
19
20function SwitchButton({onToggle}) {
21 return <button onClick={onToggle}>Toggle</button>
22}

This is prop drilling. To get the on state and toggle handler to the right places, we have to drill (or thread) props through the Switch component. The Switch component itself doesn't actually need those values to function, but we have to accept and forward those props because its children need them.

Why is prop drillingĀ good?

Did you ever work in an application that used global variables? What about an AngularJS application that leveraged non-isolate $scope inheritance (or the dreaded $rootScope šŸ˜±) The reason that the community has largely rejected these methodologies is because it inevitably leads to a very confusing data model for your application. It becomes difficult for anyone to find where data is initialized, where it's updated, and where it's used. Answering the question of "can I modify/delete this code without breaking anything?" is difficult to answer in that kind of a world. And that's the question you should be optimizing for as you code.

One reason we prefer ESModules over global variables is because it allows us to be more explicit about where our values are used, making it much easier to track values and eases the process determining what impact your changes will have on the rest of the application.

Prop drilling at its most basic level is simply explicitly passing values throughout the view of your application. This is great because if you were coming to the Toggle above and decided you want to refactor the on state to be an enum, you'd easily be able to track all places it's used by following the code statically (without having to run it) and make that update. The key here is explicitness over implicitness.

What problems can prop drillingĀ cause?

In our contrived example above, there's absolutely no problem. But as an application grows, you may find yourself drilling through many layers of components. It's not normally a big deal when you write it out initially, but after that code has been worked in for a few weeks, things start to get unwieldy for a few use cases:

  • Refactor the shape of some data (ie: {user: {name: 'Joe West'}} -> {user: {firstName: 'Joe', lastName: 'West'}})
  • Over-forwarding props (passing more props than is necessary) due to (re)moving a component that required some props but they're no longer needed.
  • Under-forwarding props + abusing defaultProps so you're not made aware of missing props (also due to (re)moving a component).
  • Renaming props halfway through (ie <Toggle on={on} /> renders <Switch toggleIsOn={on} />) making keeping track of that in your brain difficult.

There are various other situations where prop drilling can cause some real pain in the process of refactoring especially.

How can we avoid problems with prop drilling?

One of the things that really aggravates problems with prop drilling is breaking out your render method into multiple components unnecessarily. You'll be surprised how simple a big render method can be when you just inline as much as you can. There's no reason to breaking things out prematurely. Wait until you really need to reuse a block before breaking it out. Then you wont need to pass props anyway!

Fun fact, there's nothing technically stopping you from writing your entire application as a single React Component. It can manage the state of your whole application and you'd have one giant render method... I am not advocating this though... Just something to think aboutĀ :)

Note: I've written a blog post about this concept called "When to break up a component into multiple components" that you may enjoy.

Another thing you can do to mitigate the effects of prop-drilling is avoid using defaultProps for anything that's a required prop. Using a defaultProp for something that's actually required for the component to function properly is just hiding important errors and making things fail silently. So only use defaultProps for things that are not required.

Keep state as close to where it's relevant as possible. If only one section of your app needs some state, then manage that in the least common parent of those components rather than putting it at the highest level of the app. Learn more about state management from my blog post: Application State Management.

Use React's Context API for things that are truly necessary deep in the react tree. They don't have to be things you need everywhere in the application (you can render a provider anywhere in the app). This can really help avoid some issues with prop drilling. It's been noted that context is kinda taking us back to the days of global variables. The difference is that because of the way the API was designed, you can still statically find the source of the context as well as any consumers with relative ease.

Conclusion

Prop drilling can be a good thing, and it can be a bad thing. Following some good practices as mentioned above, you can use it as a feature to make your application more maintainable. Good luck!

Discuss on Twitter ā€¢ Edit post on GitHub

Share article
loading relevant upcoming workshops...
Kent C. Dodds

Kent C. Dodds is a JavaScript software engineer and teacher. He's taught hundreds of thousands of people how to make the world a better place with quality software development tools and practices. He lives with his wife and four kids in Utah.