Create a Context Hook to Handle CheckoutItems
Bring the Context API into Hardware Handler to decrease our reliance on props.
The Context API can eliminate our need to pass props to child components for checkout#
As I said when we first discussed context in the module introducing the various hooks in React, context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, etc.
With context, instead of having to pass props from a parent component down through multiple children to reach the component that needs it (a React anti-pattern referred to as "prop drilling"), we can simply wrap the parent component's state with a context Provider and call the values or functions we need in whichever child components need them with the help of the useContext
Hook.
How to know when to use context
You might be wondering how to know when to use context in your own apps, and it's a good question.
Typically, when I see component props being passed through one or more components purely to reach more deeply nested children, that's a good indicator to me that context could be useful.
If a direct descendant of a component needs state, passing it as a prop is probably fine, but much more than that, and it's worth considering context.
Don't worry; these next few lessons will help clarify how you can employ context to good effect.
In this lesson, we'll create our first context component and call the useContext
Hook to handle some of the checkout state in our code.
Sample code zip file
If you need a fresh copy of the sample app before we begin adding context, it can be downloaded here.
App.js
passes a lot of checkout state throughout the site#
When reviewing the source code for this app (especially our root component of App.js
), it appears a lot of checkout-related state is being passed as props to various child components.
Specifically, inside of the <App>
component, I see the checkoutCount
variable and the updateCheckoutCount
function being passed to multiple components.
While this is fine and works, it presents a good opportunity to employ React's Context API to make these values more globally available in the app to the components that need them without having to pass them as props.
Ready to make it happen?
First, set up a context
folder#
As with our other new additions to the app, like custom hooks, we want to keep our contexts organized. My preference for this (especially as some contexts can be used all over an application) is to have a top-level folder inside of the main src/
folder just for context files.
So the first thing I'll do for our new checkout-focused context is make a folder inside of src/
named context
.
Make a new CheckoutItemContext.js
file#
Once the context
folder's there, go ahead and create a new file called CheckoutItemContext.js
inside of the folder.
This file is where we'll instantiate our context instance and define all the default values this context will be responsible for.
Why set default context values?
If you recall from earlier modules, default values are only used when a component does not have a matching context
Provider
above it in the tree. (Personally, I've not encountered this scenario in real-world application development, but it's worth keeping in mind.)I like to define these values inside the file so that it's easier for me to quickly check a context file and know at a glance what values should be available to me.
Feel free to include or exclude it from your own context files as you get more comfortable.
Inside of our new file, add the following code:
import { createContext } from 'react';
const CheckoutItemContext = createContext({});
export default CheckoutItemContext;
This CheckoutItemContext
variable is the context object that we'll inject into our code where we define the state variables this context will keep track of (the <App>
component, in this case).
And as I said before, we can already see that the checkoutCount
and updateCheckoutCount
functions are being passed around inside of <App>
to its children, so let's add these two values to our new context file within the CheckoutItemContext
variable.
const CheckoutItemContext = createContext({
checkoutCount: 0,
updateCheckoutCount: () => {},
});
With that, we're ready to put this new context component into action.
Add CheckoutItemContext
to the <App>
#
Head over to the App.js
file in your code editor. This is where our new CheckoutItemContext
Provider will live, so we have to add it next.
Import the context#
Naturally, the first thing we'll want to do is import the context into this component. Add it up at the top of this component along with all our other imports.
import { useCheckout } from '../../hooks/useCheckout';
import CheckoutItemContext from '../../context/CheckoutItemContext';
import 'react-toastify/dist/ReactToastify.css';
Best practice: keep CSS file imports at the bottom of a component's imports
Just as an aside, it's considered a best practice to keep any CSS file imports at the bottom of a component for ease of understanding and consistency in the code.
This is why you'll always see me adjust import order if something new happens to be auto-imported into a file below a CSS file.
Wrap App's JSX with the context provider#
Once CheckoutItemContext
is imported, we'll create its Provider object that allows consuming components to subscribe to context changes.
The Provider component accepts a value
prop to be passed to consuming components that are descendants of this Provider.
Our Provider will need the local checkoutCount
and updateCheckoutCount
variables, so we'll pass those into our CheckoutItemContext.Provider
object as a comma-separated list for its value
.
Here's what the final JSX will look like with the Provider and local values added.
return (
<Router>
<ToastContainer />
<CheckoutItemContext.Provider
value={{
checkoutCount,
updateCheckoutCount,
}}
>
<section className="app-wrapper">
<Navbar checkoutCount={checkoutCount} />
<article className="app-container">
<Route exact path="/" component={Home} />
<Route path=