This is the early access documentation preview for Custom Views. This documentation might not be in sync with our official documentation.
Extending the commercetools Frontend SDK
The following template file is provided to set up the SDK and integrations. These are the key aspects of the file:
- The
CommercetoolsSDK
class andComposableCommerceEvents
type - The
CommercetoolsSDK.ts
constructor - The instance of the
CommercetoolsSDK
class
import { SDK } from '@commercetools/frontend-sdk';import {ComposableCommerce,ComposableCommerceEvents,} from '@commercetools/frontend-composable-commerce';// Add other integration's custom events to the SDK's generic type here,// by extending ComposableCommerceEvents with their type using an intersection.// For example, <ComposableCommerceEvents & OtherEvents>.// You may also add your own custom events.class CommercetoolsSDK extends SDK<ComposableCommerceEvents> {composableCommerce!: ComposableCommerce;// Add your other integrations here.constructor() {super();this.composableCommerce = new ComposableCommerce(this);// Initialize your other integrations here.this.on('errorCaught', (event) => {// Handle any integration errors caught by the SDK globally.// For example, log error, fire notification, etc.console.log('SDK error: ', event.data);});// Set up any other custom global event handlers here.// Ensure types are created and added to the SDK generic type// if specific to your project.}}// Create a single instance of the SDK.const sdk = new CommercetoolsSDK();// Export only the instance to serve as a singleton throughout the project.export { sdk };
CommercetoolsSDK class and ComposableCommerceEvents type
The CommercetoolsSDK
class extends the SDK
class from @commercetools/frontend-sdk
, which provides the CommercetoolsSDK
class with the methods and properties available in the SDK.
The ComposableCommerceEvents
type is passed to the generic SDK argument. The ComposableCommerceEvents
type comes from the @commercetools/frontend-composable-commerce
integration package and specifies the types of any custom events of this particular package. In the SDK itself, this type is combined with the StandardEvents
type with a type intersection. StandardEvents
are standard events that an e-commerce integration is likely to implement (for example, cartFetched
or productQueried
).
If an event is not defined and passed to the generic SDK argument, triggering the event or adding a handler will cause a TypeScript compilation error.
Following is the pattern for extending event types and an implementation example.
export type Events = {[key: string]: {[key: string]: unknown;};};
export type CustomEventTriggers = {someEventTriggered: { time: string };someComponentClicked: { x: number; y: number };};
CommercetoolsSDK.ts constructor
Two things happen in the CommercetoolsSDK.ts
constructor:
Passing the SDK instance to the constructor in the form of
this
initializes the@commercetools/frontend-composable-commerce
integration. This ensures that event triggers and handlers are set on the single instance of the@commercetools/frontend-sdk
SDK and that actions that are called on the integration use the same SDK instance that was configured.By default, a handler is set for the
errorCaught
event, which is triggered on the@commercetools/frontend-sdk
SDK when an error occurs. You can deactivate the handler by commenting it out. However, it is useful for global error handling, such as logging the error or triggering a notification. You can also set other globally managed custom event handlers in the constructor.The SDK doesn't throw any errors. It returns
{ data: T, isError: false }
on success or{ isError: true, error: FetchError }
on error.
Instance of the CommercetoolsSDK class
Instead of exporting the CommercetoolsSDK
class itself, an instance of the class is created and exported. This way, when the SDK is imported anywhere in the project, the same instance is used. This ensures that once configured, the SDK is set up and integrations and event handlers are constructed and added only once.
If multiple instances of the CommercetoolsSDK
class are created, the errorCaught
handler will be added continuously and executed as many times as constructed. Furthermore, some integrations may add event handlers upon construction. Creating multiple instances of integrations will cause a similar bug.
Add integrations to the SDK
It is possible to extend the SDK with the integrations you develop. To do so, set up the integrations in the CommercetoolsSDK
constructor, passing the SDK instance like the ComposableCommerce
instance.
Additionally, any custom events must be added to the generic argument of SDK in the form of an intersection, as shown in the following example.
The backend actions must be added to your backend service to extend your extensions API.
class CommercetoolsSDK extends SDK<ComposableCommerceEvents & OtherIntegrationEvents> {...}
Create custom event triggers
commercetools Frontend SDK allows creating custom event handlers and triggering them within the project, such as when clicking on a particular component or holding the pointer over it. To add custom event triggers to the SDK event definition, create a file in the packages/<project>/frontend/sdk/
folder, as shown in the following example.
export type MyCustomEvents = {marketingBannerClicked: { id: string };marketingBannerHovered: { id: string; x: number; y: number };};
Then, add the file you created to the CommercetoolsSDK
class in the generic SDK argument. The file must be added in the form of an intersection, as shown in the following example.
import { SDK } from "@commercetools/frontend-sdk";import {ComposableCommerce,ComposableCommerceEvents} from "@commercetools/frontend-composable-commerce";import { MyCustomEvents } from "./MyCustomEvents";class CommercetoolsSDK extends SDK<ComposableCommerceEvents & MyCustomEvents> {...}...
Once set, events can be triggered within your project, but you must clean up the event handlers set within components. Otherwise, handlers will stack on components remounting and may attempt to perform state updates on unmounted components, depending on the nature of the custom events.
Example of custom event trigger implementation
Following is an implementation example with the marketingBannerClicked
event. In this example, the MarketingBanner
component is given an onClick
React event handler, which calls the sdk.trigger
method.
An event constructed as the @commercetools/frontend-sdk
exported class is passed to the sdk.trigger
method. The event has the following properties, which match those previously defined in the MyCustomEvents.ts
file:
eventName: 'marketingBannerClicked'
, which matches the key of the custom event.data: { id: string }
, which matches the value type of the custom event.
The named marketingBannerClickedHandler
method is defined. In this method, an event
parameter of type Event
from the @commercetools/frontend-sdk
library takes the following generic types:
EventName
, matching the key/property name defined in theMyCustomEvents.ts
file.EventData
, matching the key's value type.
This method is defined as a named function so that it is possible to remove the handler from the SDK.
The React useEffect
component lifecycle hook is used to call sdk.on
to set up the event handler on component mount and sdk.off
to remove the handler on component unmount, passing the named handler on both occasions.
It is not required to set up event handlers within the same component, they can be set up anywhere in the project. It is often easier to set up the handlers within the CommercetoolsSDK
constructor to avoid lifecycle bugs. The purpose of the following example is to describe event lifecycle management within components.
import Image from 'next/image';import { useEffect } from 'react';import { sdk } from '../../sdk';import { Event } from '@commercetools/frontend-sdk';interface Props {marketingId: string;imageSrc: string;}const MarketingBanner = ({ marketingId, imageSrc }: Props) => {const marketingBannerClickedHandler = (event: Event<'marketingBannerClicked',{id: string;}>) => {// Perform custom event handling logic here.console.log('Marketing banner clicked, id: ' + event.data.id);};const clickHandler = (id: string) => {sdk.trigger(new Event({eventName: 'marketingBannerClicked',data: {id: id,},}));};useEffect(() => {sdk.on('marketingBannerClicked', marketingBannerClickedHandler);return () => {sdk.off('marketingBannerClicked', marketingBannerClickedHandler);};}, []);return <Image src={imageSrc} onClick={() => clickHandler(marketingId)} />;};export default MarketingBanner;