What is prop drilling in React?

Prop drilling in React

React developers often encounter scenarios where they must pass data/state from a top-level component to a deeply nested component. Prop drilling refers to transporting this data/state as props to the intended destination through intermediate components.

Example

Consider a visual representation of the component tree for a simple furniture e-commerce site to understand the problem better:

Suppose the top-level App Component has access to the state of our shopping cart; we want to update the state by changing the button color from green to grey whenever the user clicks the "Add to Cart" button.

Let's implement the scenario above to understand it. Run the code and try clicking the "Add to Cart" button.

import React from 'react';
require('./styles.css');

import ReactDOM from 'react-dom';
import App from './App.js';

ReactDOM.render(
  <App />, 
  document.getElementById('root')
);

Explanation

  • Lines 6-14: We set the state of the shopping cart and define a method changeColor that is responsible for changing state.
  • Line 23: We pass buttonColor and changeColor as props to ProductCard component.
  • Line 38: The ProductCard component then passes the props it received to the Button component.
  • Line 50: The Button component passes changeColor props to the onClick event. The event fires when the button is clicked and sets the state of button to color grey.
  • Line 51: The button element reflects the change in state by turning the backgroundColor attribute from green to grey to signal that product has been added to cart.

Issue

As can be observed from the code in the example app above, this approach is redundant and cumbersome even in a simple app. We pass state/data manually through components that don't require it, making our code cluttered and difficult to maintain. In addition, we may accidentally rename props midway through this 'drilling' process and run into bugs. These issues are compounded for large-scale applications, thus making this process infeasible.

Solution

There are three alternative solutions to solve the problem we encountered above:

  • Redux: An external library that offers state management for React applications.
  • Context: An API that enables sharing inherently global data/state with components at different nesting levels.
  • Component composition: A technique which involves passing components to other components as props. It has two subcategories:
    • Container components
    • Specialized components

For our example above, using Redux would needlessly involve an external library, and the Context API would pose issues with reusability and performance when the application is scaled up. Hence, component composition is a viable solution. The other two solutions may cater to different use cases involving prop drilling.

Code

The app below shows modified code that uses composition with container components:

import React from 'react';
require('./styles.css');

import ReactDOM from 'react-dom';
import App from './App.js';

ReactDOM.render(
  <App />, 
  document.getElementById('root')
);

Explanation

  • Lines 17–24: We put our code structure directly in the top-level component: App , which enables us to pass required props directly to Button component.
  • Line 33: We use the default children prop to render the Details and Button components within ProductCard.

Conclusion

There are different ways to solve the issue of prop drilling. This article focused on component composition as a solution due to its effectiveness and simplicity for most use cases.