As you have seen in the ‘How to create widget’s section’s Step: 1’, that we have already created a component and make sure to make a neccesary changes in respactive index.ts files as well in following Steps of it. In Step 1 of the ‘How to create widget’s section’s Step: 1’, section, you’ve observed that we’ve created a component and ensured that the necessary changes are made in the respective index.ts files as well, as outlined in the subsequent steps. When you made your Widget, you likely created a folder for the component. In this folder, you have files like a .tsx file which returns JSX, where you define how the component looks and an index.ts file that goes along with it.
src
 └── components
        ├── cms-library
            ├── exp-test-component
                ├── exp-test-component.tsx
                ├── exp-test-component-controller.tsx
                └── index.ts
For the component file, which in our scenario is named exp-test-component.tsx, it might vary based on your requirements. The file name could also be different depending on the specific component you’re creating. The component file may appear similar to the snippet below: exp-test-component.tsx
/**
 * Renders an exp-test-component component.
 * @param props - Components props.
 * @returns
 */
const ExpTestComponent = (props: any) => {
  const { id, component_content } = props;

  return <div>// ...components JSX</div>;
};

export default ExpTestComponent;
In the component’s props, you’ll receive all the traitConfig objects with their internalName keys as props. For instance, if traitConfig includes a trait like this:
{
     type: "exp_checkbox",
     displayName: "Show Heading",
     internalName: "showHeadingText",
}
Its internalName would be showHeadingText. When you need to access the selected and updated value of this trait, you can directly destructure it from the props, and you’ll obtain the value of the trait “Show Heading” in it.
const { id, showHeadingText } = props;

Default id as a prop

You’ll notice that each component receives a default prop named id. This prop is automatically provided to each component integrated with the widget. You can directly destructure it from the component’s props.

exp-test-component-controller.tsx To implement all the business logic for the component, we’ll create a component-controller file, responsible for managing all the business logic. In the context of developing a React component, it’s important to centralize all state management, including useState, useEffect, and other state manipulations, within this file exclusively. The exp-test-component.tsx will solely focus on rendering JSX based on the state managed in the controller file. So, at this point, we’ll retrieve the data of the selected Content Model record. The selection is made from the pop-up of the trait type exp_contentModalPopUp. This trait returns data in terms of the selected record’s id, from which we’ll fetch the record’s data by making an API call in the controller file. So, basic struture look’s like for the each controller file as below snippet.
const ExpTestComponentController = (props: any) => {
  const { id, contentModel, modelInternalName } = props;

  /* This key need's to be unique for all the component you create for the Drag-&-Drop.
   * const modelKeyForSSR = "tes-component-ssr";
   */

  /* In this context, a reducer has been implemented to efficiently manage and handle
   *  multiple states simultaneously, thus streamlining the process of state manipulation.
   */
  const {
    componentDataDispatcher,
    setComponentDataDispatcher,
    isComponentLoaded,
  }: any = ExpComponentDataDispatcher({
    id,
    modelInternalName,
    modelKeyForSSR: modelKeyForSSR,
  });

  let parsedContentModel: ContentModelDataInterface | undefined;

  if (contentModel?.trim().length)
    parsedContentModel = JSON.parse(contentModel);

  /* You will find this useEffect which looks same in each component in which is
   * responsible for making an utilizig a common fuinction which gets data data of selected
   * record baised on the "internalName" and the "parsedContentModel".
   */

  useEffect(() => {
    if (isComponentLoaded) {
      setComponentDataDispatcher({
        type: expCommonDispatcherKeys.fetchingData,
      });
      if (contentModel?.trim()?.length) {
        (async () => {
          setComponentDataDispatcher({
            type: expCommonDispatcherKeys.dataFetched,
            data: await (parsedContentModel,
            modelInternalName,
            modelKeyForSSR,
            id),
          });
        })();
      }
    }
  }, [contentModel]);
};

return {
  componentDataDispatcher,
  contentModel,
};

export default ExpTestComponentController;

ExpComponentDataDispatcher

This serves as a standard dispatch method, offering a dispatcher. Within this common utility, we’ve integrated a ‘useReducer’ hook. Primarily, it accepts id, modelInternalName, and modelKeyForSSR as arguments. As a result, it furnishes three essential components crucial for component implementation: componentDataDispatcher, setComponentDataDispatcher, and isComponentLoaded. Here’s a breakdown of the provided elements:
  • componentDataDispatcher: This object contains two values: {isLoading, componentData}.
    • isLoading: This state is a boolean value (true/false) indicating whether the component’s data is being fetched or not. Different states utilized within the useEffect update the value of this state.
    • componentData: This state holds the data of the record obtained after making an API call, facilitated by the common method ‘getContentLibraryData’.
  • setComponentDataDispatcher: This serves as a setter method for the state, similar to when using useState. You specify an object containing type and state values, which are then set by the reducer.
  • isComponentLoaded: This state holds a boolean value (true/false), responsible for indicating whether the component’s data has already been fetched or not.

In conclusion, this controller returns:
{
     componentDataDispatcher,
     contentModel,
}
So, let’s utilize this values in component, after that component should look like,
/**
 * Renders an exp-test-component component.
 * @param props - Components props.
 * @returns
 */
const ExpTestComponent = (props: any) => {
  const { componentDataDispatcher, contentModel } =
    ExpTestComponentController(props);

  return <div>// ...components JSX</div>;
};

export default ExpTestComponent;
Now, you can utilize the componentDataDispatcher, which contains the isLoading and componentData state values. You can utilize the componentDataDispatcher’s isLoading, componentData, and contentModel to display the loader of the component or a message indicating to “Select a record”. All the data required for the component will reside in the componentDataDispatcher’s componentData key. You can utilize this data to render the component and return JSX accordingly. With Experro Storefront, integrating a component with a widget is a breeze, and customizing it to your exact specifications couldn’t be simpler.