Site Config Pattern
The Site Config Pattern is a standardized method for retrieving entry data in advance of the server-side render, utilizing Redux and Redux Sagas.
The “Site Config” refers to a global site configuration entry, typically used for managing data that is required across the entire site, such as header navigation, footer menus, social media links, and other global settings.
What is a Site Config used for?
A Site Config is useful for any data that is global and needs to be available across all pages of the site. Examples include:
- The website’s
<title>tag - A list of social media links
- Configuration for the main navigation or footer menus
- Global settings or metadata that are shared across multiple pages
How is it set up?
The React Starter project includes an implementation of the Site Config pattern as standard. It is located in the siteConfig directory. The pattern consists of the following key components:
- Redux Saga: Handles server-side data fetching for the site configuration entry.
- TypeScript Mapper: Transforms the raw entry data from the CMS into a format suitable for use in React components.
- Redux Slice: Manages the state of the site configuration, including loading, error handling, and storing the mapped data.
Additionally, the getSiteConfigSaga is invoked in the onRouteLoad function within withEvents.ts to ensure the site configuration data is fetched on every route load, if necessary.
Key Components of the Site Config Pattern
1. Redux Slice (siteConfig.slice.ts)
The Redux slice is responsible for managing the state of the site configuration. It includes:
- Initial State: Defines the default state of the site configuration, including
isLoading,isReady,isError, andmappedEntry. - Reducers:
getSiteConfig: Sets theisLoadingflag totruewhen the saga starts fetching the site configuration.setSiteConfig: Updates the state with the mapped site configuration data and setsisReadytotrue.getSiteConfigError: Handles errors during the fetching process by setting theisErrorflag and storing the error details.
- Selectors: Provides utility functions to access specific parts of the state, such as
selectSiteConfigReadyandselectSiteConfigEntry.
When extending the site configuration, update the initialState to reflect the new fields and review whether additional selectors are needed to expose them consistently to components.
2. Redux Saga (sagas.ts)
The getSiteConfigSaga is the core of the pattern. It handles the asynchronous fetching of the site configuration entry from the CMS using the contensis-delivery-api. Key steps include:
- Checking State: The saga first checks if the site configuration is already loaded using the
selectSiteConfigReadyselector. - Building the Query: A
Queryobject is created to fetch the site configuration entry. The query uses:sys.versionStatusto fetch the correct version of the entry (e.g., published or preview).sys.contentTypeIdto specify the content type of the site configuration entry.siteConfigFieldsto define the fields to retrieve.
- Fetching Data: The
api.searchmethod is used to fetch the entry data. - Mapping Data: The
siteConfigMappertransforms the raw entry data into a format suitable for the Redux state. - Updating State: The saga dispatches either
setSiteConfig(on success) orgetSiteConfigError(on failure) to update the Redux state.
::info When adding new fields to the site configuration entry, update the siteConfigFields array in fields.schema.ts so they are included in the search query. :::
3. TypeScript Mapper (siteConfig.mapper.ts)
The mapper transforms the raw site configuration entry data into a format that is easier to use in React components. For example:
export const siteConfigMapper = (
config: ContentTypeSiteConfiguration,
): SiteConfigMappedProps => {
return {
title: config.entryTitle,
};
};
Ensure the ContentTypeSiteConfiguration type is kept in sync with the corresponding CMS Content Type so the mapper can safely transform all required fields.
4. Route Integration (withEvents.ts)
The getSiteConfigSaga is invoked in the onRouteLoad function within withEvents.ts. This ensures that the site configuration is fetched on every route load, if necessary.
yield all([call(getSiteConfigSaga, ssr)]);
How to Update the Site Config Pattern
If you need to update or extend the Site Config pattern, follow these steps:
- Add New Fields to the Site Configuration:
- Update the
siteConfigFieldsarray infields.schema.tsto include the new fields you want to fetch from the CMS.
- Update the
- Update the Mapper:
- Modify the
siteConfig.mapper.tsfile to map the new fields from the CMS entry to theSiteConfigMappedPropstype. - Update the
ContentTypeSiteConfigurationandSiteConfigMappedPropstypes to include the new fields.
- Modify the
- Update the Redux Slice:
- If necessary, update the
initialStatetype insiteConfig.slice.tsto include new state properties. - Add new reducers if additional state management logic is required.
- If necessary, update the
- Update the Saga:
- Modify the
getSiteConfigSagainsagas.tsto handle any changes to the query or additional logic for fetching the new fields.
- Modify the
By following these steps, you can effectively update and extend the Site Config pattern to meet the evolving needs of your application. This pattern ensures that global data is consistently and efficiently managed across your React application.
Accessing Data Using Redux Selectors
Once the Site Config data is stored in Redux, you can access it using Selectors, which extract specific data from the state.
Example: Accessing Social Media Links
Here’s a sample API response from the site config pattern:
{
"linkedinLink": "https://www.linkedin.com/showcase/contensis",
"twitterLink": "https://twitter.com/contensis?lang=en",
"facebookLink": "https://en-gb.facebook.com/",
"entryTitle": "Leif",
"copyrightName": "Leif"
}
Using the Site Config Mapper, you can transform this data into a structured format:
export const siteConfigMapper = (
config: ContentTypeSiteConfiguration,
): SiteConfigMappedProps => {
return {
title: config.entryTitle,
social: {
linkedin: config.linkedinLink,
twitter: config.twitterLink,
facebook: config.facebookLink,
},
};
};
Ensure ContentTypeSiteConfiguration, SiteConfigMappedProps, and the initialState are updated to reflect any changes.
To access the social object, create a selector:
export const selectSocial = (state: ReduxState) =>
state.siteConfig?.mappedEntry?.social;
Using the Selector in a Component
With the selector in place, use the useSelector hook to access the social object in a React component:
import React from "react";
import { useSelector } from "react-redux";
import { selectSocial } from "~/redux/siteConfig/siteConfig.slice";
const SocialList = () => {
const social = useSelector(selectSocial);
if (!social) return null;
return (
<div>
<a href={social.linkedin} target="_blank" rel="noopener noreferrer">
LinkedIn
</a>
<a href={social.twitter} target="_blank" rel="noopener noreferrer">
Twitter
</a>
<a href={social.facebook} target="_blank" rel="noopener noreferrer">
Facebook
</a>
</div>
);
};
export default SocialList;
Key Steps to Add New Data
- Update the CMS Content Type: Add new fields in the CMS.
- Update
siteConfigFields: Add the new fields infields.schema.ts. - Update the Mapper: Modify
siteConfig.mapper.tsto map the new fields. - Update the Redux State: Update
siteConfig.slice.tsif new state properties are needed. - Create a Selector: Add a selector to access the new data.
- Use the Selector: Use
useSelectorin your components to access and render the data.