In the last lesson, we saw different scenarios where prop drilling could occur.
Let's see how we can tackle the issue of prop drilling through an example.
At present, we can only add products to the cart from the product listing page. As the next step, consider adding this feature to the product details page. To make this possible, we can include the AddToCart component in the Product component. This will require us to lift the cartItems state to the App component so that the Header and AddToCart components within the Product component can also access the necessary props.
In this scenario, we'll have to pass the props from the App component through the component chain to reach the Header and AddToCart components, even though the intermediate components have no use of these props:
React provides a feature called context to avoid this hassle of prop drilling. Context lets the parent component make some information available to any component in the tree below it, no matter how deep, without passing it explicitly through props.
Let's understand the usage of context by adding the add-to-cart feature to the product details page.
As the first step, we will create a context for the cartItems. Create a file CartItemsContext.js inside the src/contexts directory:
Inside this file, we will create and export a context named CartItemsContext using the createContext function from React. The createContext function returns a context object:
In the App component, we can create the cartItems state and make it accessible throughout the application by wrapping the components with Provider from the context. The Provider component accepts a value prop to specify the value of this context for all components inside it:
The value we passed to the context provider will be available to any component that comes under the context provider in the component hierarchy.
Now, we can use the cartItems state and setCartItems function from any component in our application using the useContext hook from React. The useContext hook accepts the context object as the argument and returns the context value.
Let's remove the cartItemsCount prop from the Header component. Instead, we can get the cartItems from the context and find its length:
Next, we can remove the props isInCart and toggleIsInCart of the AddToCart component. Instead, we can pass the slug of the product from the ProductList and Product components to AddToCart and use it along with the CartItemsContext to check whether a product is in the cart and update the cart accordingly.
Now, you can remove the cartItems state from the ProductList component. Also, remove the toggleIsInCart function and all its usages along with the usages of the isInCart prop. You can use the AddToCart component within the ProductListItem and Product components by passing the slug. Since the prop and its value has the same name, we can use the spread syntax mentioned in this lesson to pass the slug prop.
After making the above changes, you should be able to add and remove a product from the product listing and product details page without prop drilling:
Since the AddToCart component is now used by both the ProductListItem and Product components, let's move the AddToCart component to the commons directory:
Make sure to update the imports. Let's commit the new changes:
You can verify the changes here.