DEV Community

Cover image for Understanding Prop Drilling and How to Solve It
Paulo Messias
Paulo Messias

Posted on

Understanding Prop Drilling and How to Solve It

What is Prop Drilling?

"Prop drilling" is a term used in React development to describe the process of passing data from a parent component to a child component, and so on, through multiple levels of components. This can become problematic when you need to pass data or functions to components that are several levels down the component tree, resulting in code that is difficult to manage and maintain.

Example of Prop Drilling

Let's start with a simple example to illustrate the problem of prop drilling, now with TypeScript.

import React from 'react';

interface User {
  name: string;
  age: number;
}

interface ParentComponentProps {
  user: User;
}

const App: React.FC = () => {
  const user: User = {
    name: 'Paulo',
    age: 30,
  };

  return (
    <div>
      <ParentComponent user={user} />
    </div>
  );
};

const ParentComponent: React.FC<ParentComponentProps> = ({ user }) => {
  return (
    <div>
      <ChildComponent user={user} />
    </div>
  );
};

const ChildComponent: React.FC<ParentComponentProps> = ({ user }) => {
  return (
    <div>
      <p>Name: {user.name}</p>
      <p>Age: {user.age}</p>
    </div>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

In this example, the user data is passed from App to ParentComponent, and then to ChildComponent. Although this example is simple, in a larger application, there may be many levels of components, making the code difficult to maintain.

Solutions to Prop Drilling

There are several approaches to solving the prop drilling problem. Let's explore two common solutions: the Context API and the useReducer hook.

Using the Context API

The React Context API allows you to share data between components without the need to manually pass props at every level.

import React, { createContext, useContext } from 'react';

interface User {
  name: string;
  age: number;
}

const UserContext = createContext<User | undefined>(undefined);

const App: React.FC = () => {
  const user: User = {
    name: 'Paulo',
    age: 30,
  };

  return (
    <UserContext.Provider value={user}>
      <ParentComponent />
    </UserContext.Provider>
  );
};

const ParentComponent: React.FC = () => {
  return (
    <div>
      <ChildComponent />
    </div>
  );
};

const ChildComponent: React.FC = () => {
  const user = useContext(UserContext);

  if (!user) {
    return null;
  }

  return (
    <div>
      <p>Name: {user.name}</p>
      <p>Age: {user.age}</p>
    </div>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

With the Context API, the user is made available to any component within the UserContext.Provider without needing to be passed as a prop.

Using useReducer

The useReducer hook is useful for managing complex states in React functional components. It can be combined with the Context API to avoid prop drilling.

import React, { createContext, useContext, useReducer, Dispatch } from 'react';

interface User {
  name: string;
  age: number;
}

interface UserState {
  user: User;
}

type Action =
  | { type: 'UPDATE_NAME'; payload: string }
  | { type: 'UPDATE_AGE'; payload: number };

const UserContext = createContext<{
  state: UserState;
  dispatch: Dispatch<Action>;
} | undefined>(undefined);

const initialState: UserState = {
  user: {
    name: 'Paulo',
    age: 30,
  },
};

const userReducer = (state: UserState, action: Action): UserState => {
  switch (action.type) {
    case 'UPDATE_NAME':
      return {
        ...state,
        user: {
          ...state.user,
          name: action.payload,
        },
      };
    case 'UPDATE_AGE':
      return {
        ...state,
        user: {
          ...state.user,
          age: action.payload,
        },
      };
    default:
      return state;
  }
};

const App: React.FC = () => {
  const [state, dispatch] = useReducer(userReducer, initialState);

  return (
    <UserContext.Provider value={{ state, dispatch }}>
      <ParentComponent />
    </UserContext.Provider>
  );
};

const ParentComponent: React.FC = () => {
  return (
    <div>
      <ChildComponent />
    </div>
  );
};

const ChildComponent: React.FC = () => {
  const context = useContext(UserContext);

  if (!context) {
    return null;
  }

  const { state, dispatch } = context;

  return (
    <div>
      <p>Name: {state.user.name}</p>
      <p>Age: {state.user.age}</p>
      <button onClick={() => dispatch({ type: 'UPDATE_NAME', payload: 'João' })}>
        Change Name
      </button>
      <button onClick={() => dispatch({ type: 'UPDATE_AGE', payload: 35 })}>
        Change Age
      </button>
    </div>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

With useReducer and the Context API, you can manage complex states and make data and functions available to any component within the context without prop drilling.

Conclusion

Prop drilling can complicate the code of a React application, especially as the application grows. Fortunately, there are several ways to solve this problem, including using the React Context API or the useReducer hook. These solutions help make the code cleaner, easier to maintain, and more scalable.

Top comments (7)

Collapse
 
sheraz4194 profile image
Sheraz Manzoor

I have come with even better solution, using zustand, this can resolve this matter on a high level. Checkout this article dev.to/sheraz4194/simplifying-stat...

Collapse
 
paulocappa profile image
Paulo Messias

Good approach!

Collapse
 
rkogan profile image
Robert Kogan

I prefer using useState and hooks to solve this issue, much easier to understand and trace than using the context API, IMHO.

Collapse
 
litlyx profile image
Antonio | CEO at Litlyx.com

Great work!

Collapse
 
gerrell_fontenette_33d118 profile image
Gerrell Fontenette

I started off learning javascript, but the mere fact there so many frameworks and libraries and forever changes at a fast pace. I decided to go with .net and c#

Collapse
 
arslanovngc profile image
Ismatillo

Component composition could be a nice solution

Collapse
 
paulocappa profile image
Paulo Messias

Yes, thanks for your comment! I’ll write a post about component composition soon. Please, stay tuned!

Some comments may only be visible to logged-in visitors. Sign in to view all comments.