Stop Prop Drilling ! Do this instead !

react web application javascript

What is Prop Drilling ?

Prop drilling, also known as "prop drilling" or "props tunneling", refers to the process of passing props from a parent component to a deeply nested child component in a React application. The main disadvantage of prop drilling is that it can make the component structure of a React application more complex and difficult to understand.

Additionally, it can also make it more difficult to reuse components and can lead to increased maintenance costs. It can also make it difficult to refactor or debug the application, as changes to the props passed through multiple levels of components can have unintended consequences.

Example of prop drilling

In this example, the Parent component passes a user prop to the ChildA component, which then passes it on to the ChildB component, which then passes it on to the ChildC component. This means that the user prop has to be passed through three levels of components in order for the ChildC component to access it. This can make it difficult to understand the component structure of the application and make it more difficult to reuse or refactor components.

// Parent component
function Parent(props) {
  return (
    <div>
      <ChildA user={props.user} />
    </div>
  );
}

// Child component A
function ChildA(props) {
  return (
    <div>
      <ChildB user={props.user} />
    </div>
  );
}

// Child component B
function ChildB(props) {
  return (
    <div>
      <ChildC user={props.user} />
    </div>
  );
}

// Child component C
function ChildC(props) {
  return <div>Hello, {props.user.name}!</div>;
}

How to avoid prop drilling ?

The Provider pattern is a way to share state and behavior across multiple components in a React application. It is a powerful tool for managing complex state and reducing the amount of props drilling required in large applications.

The basic idea behind the Provider pattern is to create a top-level component that serves as the "provider" of state and behavior to all of the child components that need it. This provider component holds the state and methods that the child components will need, and it makes them available to the children through the use of context.

To implement the Provider pattern in a React application, you would first create a new component that will serve as the provider. This component should hold the state and methods that will be shared with the child components. It should also have a method for updating the state when it changes.

Next, you would wrap the top-level component of your application in the provider component. This allows the provider to make its state and methods available to all of the child components within the application.

To access the state and methods provided by the provider, child components can use the useContext hook to get the context from the provider. This allows the child component to access the state and methods without having to pass them down through props.

It's important to note that the Provider pattern should be used judiciously, as it can make your application more complex and harder to reason about if overused. It's best used in cases where the state or behavior being shared is truly global and needed by many components, rather than just a few.

Overall, the Provider pattern is a powerful tool for managing state and behavior in React applications. It allows for a clean separation of concerns and can make large applications easier to reason about.

Here is an example of using the Provider pattern in a React application:

import React, { useState } from 'react';

const UserContext = React.createContext();

function UserProvider(props) {
  const [user, setUser] = useState({ name: 'John Doe' });

  function updateUserName(newName) {
    setUser({ name: newName });
  }

  return (
    <UserContext.Provider value={{ user, updateUserName }}>
      {props.children}
    </UserContext.Provider>
  );
}

function App() {
  return (
    <UserProvider>
      <div>
        <UserNameDisplay />
        <ChangeNameForm />
      </div>
    </UserProvider>
  );
}

function UserNameDisplay() {
  const { user } = React.useContext(UserContext);

  return <div>{user.name}</div>;
}

function ChangeNameForm() {
  const { updateUserName } = React.useContext(UserContext);

  const handleSubmit = (event) => {
    event.preventDefault();
    updateUserName(event.target.name.value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" name="name" placeholder="Enter new name" />
      <button type="submit">Change Name</button>
    </form>
  );
}

In this example, the UserProvider component is the provider that holds the state for the user's name. It also has a method updateUserName which updates the state. The UserNameDisplay component and the ChangeNameForm component are both child components that need access to the user's name state and the updateUserName method. They use the useContext hook to get the context from the UserProvider and access the state and method without passing them down through props.

It's worth noting that using context like this is not always the best solution and it depends on the specific use case and the size of the application.