So what’s react context ?
As defined in React docs
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
The problem that arises when passing props acrossing components that are multiple levels deep in the inheritance hierarchy is the so-called “prop-drilling”.
Say we have an interface for an object called ICart
interface ICart {
slider_enabled: boolean
settings: CartSettings
}
interface CartSettings {
[CartNavigationEnum.design]: CartDesignSettings
/// other settings
}
and we have a component hierarchy in an application that uses a card editor to visually change the look of a shopping cart that looks like this:
<CartEditor cart={cart} />
|
|
<CartEditorNavigation cart={cart} />
|
|
<SelectedSettingsComponent cart={cart}>
|
|
<CartEditorAnnouncement cart={cart}>
|
|
<FeatureActivity enabled={cart.announcement.enabled}>
As we see the Feature Activity component is concerned whether the cart announcement is enabled. From this hierarchy we need to pass our state variable cart - multiple levels deep which results in prop-drilling. A better approach would be to create a custom React context, and use the specific pieces of the object that are relevant for the component.
interface CartContextProps {
cart: ICart
saved: boolean
loading: boolean
}
const CartContext = React.createContext<CartContextProps>({
cart: initial_cart,
saved: true,
loading: false,
})
Here we create a context object that stores 3 specific state variables, the cart object, saved boolean, whether the cart editing is saved or not and loading boolean variable.
Then we need to create a provider component that will expose all these variables to its children
const CustomCartContextProvider = (props: any) => {
return (
<CartContext.Provider
value={{
cart: tempCart,
saved: isSaved,
loading,
}}
>
{props.children}
</CartContext.Provider>
)
}
After this step is completed we need to wrap the root CartEditor component with our custom context provider component.
<CustomCartContextProvider>
<CartEditor />
</CustomCartContextProvider>
So now insted of having to pass props in the inheritance hierarchy we can just use the special useContext hook provided by the react library and destructure the parts of the context we’re interested in.
// CartEditorAnnouncement.tsx
const CartEditorAnnouncment = () => {
const {
cart: {
settings: { announcements },
},
onAnnouncementSettingsChange,
} = useContext(CartContext)
return <FeatureActivity enabled={announcements.enabled} />
}
In this case by using the context we have decoupled the coupling between components in the component inheritance hierarchy.