In programming, we've become adept at the practice of encapsulation — a cornerstone principle where we carefully compartmentalize our code. By doing so, we make each module independent, ensuring they function autonomously within a larger system. This approach not only enhances organization but also promotes scalability and robustness in our digital architectures.
As crucial as encapsulation is, effective communication between components remains essential. In any sophisticated system, components must seamlessly interact, sharing and manipulating shared data. This exchange encourages cohesion and ensures that each element operates harmoniously within the larger framework.
Similarly, when we develop our renderings in Sitecore, we usually encapsulate each component and treat them independent from one another. But there will be times where we need our components to communicate between each other. A good example for when we might need to have data shared between components are the ff:
These are just a few examples, and the data sharing requirement may vary depending on the application’s complexity and nature.
A simple answer to solving this problem is Context. This is a useful feature React provides us which help improve accessing information without having to manually drill in the data through props on components. There are two ways you can use React Context and help you solve problems you might be facing in your Sitecore Projects that can actually be solved using React Context.
When we talk about Global Context, it’s a strategy of using React Context at a Layout level. This helps challenges where certain data might be needed at a site level. Some good examples of this are the following:
There are even more types of projects that deal with global data being used but the examples above a good number of examples.
Let’s start by creating our custom hook. We want to create a custom hook instead of adding the context right away is to separate the code from where we’re trying to add it to, in this case, it is our Layout.tsx
**file. We can name our custom hook file useGlobalContext.tsx
. In our little experiment we want to add a toggle between light and dark mode. The code we have below contain the types, the context, context provider and the custom hook** we are using.
type GlobalSettingsType = {
isDarkMode: boolean;
setIsDarkMode: React.Dispatch<React.SetStateAction<boolean>>;
};
type GlobalSettingsProviderProps = {
children: ReactNode;
};
const GlobalSettingsContext = createContext<GlobalSettingsType | undefined>(undefined);
export const GlobalSettingsProvider: React.FC<GlobalSettingsProviderProps> = ({ children }) => {
const [isDarkMode, setIsDarkMode] = useState(false);
return (
<GlobalSettingsContext.Provider
value={{
isDarkMode,
setIsDarkMode,
}}
>
{children}
</GlobalSettingsContext.Provider>
);
};
export const useGlobalSettingsContext = () => {
const context = useContext(GlobalSettingsContext);
if (!context) {
throw new Error('GlobalSettingsProvider is not found.');
}
return context;
};
Now that we have this in place, we can use this inside the Layout.tsx
file. If you need to, you can wrap all of the three placeholders, headless-header
, headless-main
, and headless-footer
.
<GlobalSettingsProvider>
{/* root placeholder for the app, which we add components to using route data */}
<div className={mainClassPageEditing}>
<header>
<div id="header">
{route && <Placeholder name="headless-header" rendering={route} />}
</div>
</header>
<main>
<div id="content">
{route && <Placeholder name="headless-main" rendering={route} />}
</div>
</main>
<footer>
<div id="footer">
{route && <Placeholder name="headless-footer" rendering={route} />}
</div>
</footer>
</div>
</GlobalSettingsProvider>
With this in place, in any of your components you can call useGlobalSettingsContext
and access a shared site value that can be processed by any child component.
export const Default = (props: ComponentProps) => {
const { isDarkMode, setIsDarkMode } = useGlobalSettingsContext();
return (
...
);
};
When we’re dealing with complex components, for example, we are required to make a filter that affects multiple item lists. Now these item lists are also separate components added on to the page. The difference between this and the global context is that this logic only applies to this set of components and nothing else, so globally set context doesn’t fit the need for this.
To solve this, we can create a separate component with placeholder setup. This will be a wrapper or container component that creates the context consumed by the components added inside the placeholder.
With the example above, you might already have an idea of how you can accomplish this task. I’ll go directly into completing the setup for the custom hook usePageListingFilter
with a provider named PageListingFilterProvider
which should contain a category value of the items it should only allow to be visible. Let’s create the component that will use this.
export const Default = ({ fields, rendering, params }: PageListingFilterProps) => {
const phKey = `pagelistings-${params.DynamicPlaceholderId}`;
return (
<PageListingFilterProvider>
<div>
<Text tag="h3" className="text-neutral-100" field={fields?.heading} />
<Placeholder name={phKey} rendering={rendering} />
</div>
</PageListingFilterProvider>
);
};
Since we’ve wrapped the provider around the placeholder, we can now access the context within any component that is added under the pagelistings
placeholder. The code looks pretty similar with the Global Context we created. The only difference is how and why we applied the context.
Every developer has a different set of challenges to overcome and we might not share or experience the obstacles you have for your project, but I have tried to present a general idea of how you can utilize React Context to solve a number of challenges you might experience in your development career. It might not be exactly as what you need but do some discovery and some experimenting and maybe this may be the solution you need. Check out other blogs the team has written, we have a couple of them, and you might be able to read one that will fix your problem.