Working with props is great! They provide simple mechanism to pass state and other information with components in the hierarchy.
Similar to what we have seen earlier with lifting state in React, what if we want to pass information from a top level component to children probably at the bottom of that tree?
One way is to keep passing the required information through props from subsequent children to the ones which actually needs it. But it may not be an optimal solution, because components who do not need access to that information and have to play role of middleware unnecessarily. This situation is called as "Prop Drilling" in React.
Prop Drilling even though its not a official term has a solution in the form of Context API or with Redux.As you can see above, we can provide a context from the root element of the component tree and the children(s) which needs access to use can use the context created earlier.
When we use Context API to create a context, it returns a provider and consumer.
We can set a default context value as well. Provider works as a distributor. Since the introduction of useContext hook, we no longer need consumer.
To use the context API, find a root component which has the information. Create a context at the root component and setup the provider at the root of the component tree so it can wrap all the children which needs information
It is recommended to use Context API where props needs to be passed down at-least 3-4 levels down, Otherwise, props should be used.
Creating Context
The context object should be a global object so that any children can access it.
//create global context
const BookLibraryContext = createContext();
Setting up Provider at Root Component
To set values required by children, use a property exposed by Provider called as value. We can use any type of value to provide the needed abstraction. This property can hold functions, objects, arrays etc.
//value property can hold any type of value which will be exposed to children
<BookLibraryContext.Provider value={{ purchasedBooks }}>
<section className="booklist">
<h1 className="display-4">Welcome to the React Library!</h1>
<Purchase purchasedBooks={purchasedBooks} />
</section>
</BookLibraryContext.Provider>
useContext
useContext is a hook and similar to every hook, it should be imported as a named import.
Create an object of useContext and pass the context object to access the context, in our case, its BookLibraryContext.
Make sure the context object is correctly destructured as per the value set by the parent or root. Child components can pick and choose what they want from the context.
//create a context object for the BookLibraryContext
const { purchasedBooks } = useContext(BookLibraryContext);
JS File
Notice how the component tree looks like, similar to our problem statement, where we wanted to pass props from BookLibrary to Inventory without actually passing props through children.
//create global context
const BookLibraryContext = createContext();
//Parent
export const BookLibrary = () => {
//moving state Up!
const books = [
{
id: 3,
title: "The Alchemist",
author: "Paulo Cohlo",
coverImage: "./images/the-alchemist.jpg",
},
{
id: 4,
title: "Grandma's Bag of Stories",
author: "Sudha Murthy",
coverImage: "./images/grandams-bag-of-stories.jpg",
},
{
id: 5,
title: "The Power of Your Subconscious Mind",
author: "Joseph Murphy",
coverImage: "./images/the-power-of-your-subconscious-mind.jpg",
special: true,
},
];
const [purchasedBooks, setPurchasedBooks] = useState(books);
return (
<>
<BookLibraryContext.Provider value={{ purchasedBooks }}>
<section className="booklist">
<h1 className="display-4">Welcome to the React Library!</h1>
<Purchase purchasedBooks={purchasedBooks} />
</section>
</BookLibraryContext.Provider>
</>
);
};
//Child 2 (Sibling 2)
const Purchase = () => {
return (
<>
<h1>Verifying Purchase Activies... </h1>
<Address />
</>
);
};
const Address = () => {
return (
<>
<h2>Before sending your books to your address</h2>
<Inventory />
</>
);
};
const Inventory = () => {
//create a context object for the BookLibraryContext
const { purchasedBooks } = useContext(BookLibraryContext);
return (
<>
<div className="row">
<h3>Checking Inventory for your purchased books below:</h3>
<div className="col-md-4"></div>
<div
className="col-md-4 add-column-margin"
style={{ marginTop: 30, maxHeight: 250 }}
>
<div id="myCarousel" className="carousel slide" data-ride="carousel">
<ol className="carousel-indicators">
{purchasedBooks.map((book, index) => {
return (
<li
key={index}
data-target="#myCarousel"
data-slide-to={index}
className={index === 0 ? "active" : ""}
></li>
);
})}
</ol>
<div className="carousel-inner">
{purchasedBooks.map((book, index) => {
return (
<div
key={index}
className={
index === 0 ? "carousel-item active" : "carousel-item"
}
>
<img
className="d-block w-100"
src={book.coverImage}
alt="First slide"
></img>
<div class="carousel-caption d-none d-md-block">
<h5
style={{
color: "white",
backgroundColor: "rgba(0, 0, 0, 0.6)",
}}
>
{book.title}
</h5>
</div>
</div>
);
})}
</div>
<a
className="carousel-control-prev"
href="#myCarousel"
role="button"
data-slide="prev"
>
<span
className="carousel-control-prev-icon"
aria-hidden="true"
></span>
<span className="sr-only">Previous</span>
</a>
<a
className="carousel-control-next"
href="#myCarousel"
role="button"
data-slide="next"
>
<span
className="carousel-control-next-icon"
aria-hidden="true"
></span>
<span className="sr-only">Next</span>
</a>
</div>
</div>
</div>
</>
);
};
ReactDOM.render(<BookLibrary />, document.getElementById("root"));
Output
Comments
Post a Comment