Skip to content

Component Factory

Wei Zhe edited this page Dec 11, 2024 · 2 revisions

The Component Factory is a factory design pattern used to dynamically generate React components based on specific configuration settings. It allows for the creation of flexible, customizable user interfaces where components can be conditionally rendered depending on the context, such as vote event configurations or user inputs.

This pattern makes it easier to manage different components without having to manually handle conditional logic for rendering. Instead, a configuration object is used to specify which components should be rendered and when.

Key Concepts

image

  1. ComponentConfig: Defines the configuration for each component. This includes:

    • Component: The React component to render.
    • key: A unique key used for identifying the component instance.
    • getProps: A function that takes baseProps and returns the props to be passed to the component.
    • condition: An optional function that takes baseProps and returns a boolean. If this function returns false, the component is excluded from rendering.
  2. FactoryConfig: Contains a list of ComponentConfig items. This object is passed to the factory to create a set of dynamic components.

  3. Dynamic Component Factory: The createDynamicComponentFactory function uses the configuration provided in FactoryConfig to generate the appropriate components based on the provided baseProps.

How It Works

The createDynamicComponentFactory function takes in a FactoryConfig object and returns an object with a method generateItems. This method accepts baseProps, which are passed to each component's getProps function. The resulting components are rendered based on the conditions defined in the condition function of each ComponentConfig.

createDynamicComponentFactory Function

export const createDynamicComponentFactory = (config: FactoryConfig) => ({
  generateItems: (baseProps: any) => {
    return config.items
      .filter(
        (componentConfig) =>
          !componentConfig.condition || componentConfig.condition(baseProps)
      )
      .map(({ Component, key, getProps }) => (
        <Component key={key} {...getProps(baseProps)} />
      ));
  },
});

Parameters:

  • config: A configuration object that contains a list of components (items) to be dynamically rendered.
  • baseProps: The base properties passed to each component. These can contain information such as the candidates in a vote, the status of the vote, or user selections.

Returns:

  • An array of React components that match the condition specified in their respective condition function.

generateItems Function

image

  1. The generateComponents function of a respective component factory is called
  2. For each ComponentConfig which is an array present in the FactoryConfig, check the condition to be rendered. If the condition is true, then get the required props needed for the component and create it with the props.
  3. Return all generated components

Example Usage

Candidate Display Factory

The following example demonstrates how the Component Factory is used to render different candidate display formats based on a configuration object. In this case, the configuration allows the system to display candidates in a Gallery, Table, or None format.

Step 1: Define Component Configurations

Each candidate display format is defined in the candidateDisplayConfig object. The condition function checks if the voteConfig.displayType matches the required display type (Gallery, Table, or None).

const candidateDisplayConfig: FactoryConfig = {
  items: [
    {
      Component: VotingGalleryGrid,
      key: "voting-gallery-grid",
      getProps: (baseProps) => ({
        candidates: baseProps.candidates,
        selectedCandidates: baseProps.selectedCandidates,
        status: baseProps.status,
        setSelectedCandidates: baseProps.setSelectedCandidates,
        isDisabled: baseProps.isDisabled,
      }),
      condition: (baseProps) =>
        baseProps.voteConfig.displayType === DISPLAY_TYPES.GALLERY,
    },
    {
      Component: VoteCandidateTable,
      key: "vote-candidate-table",
      getProps: (baseProps) => ({
        candidates: baseProps.candidates,
        selectedCandidates: baseProps.selectedCandidates,
        status: baseProps.status,
        setSelectedCandidates: baseProps.setSelectedCandidates,
        isDisabled: baseProps.isDisabled,
      }),
      condition: (baseProps) =>
        baseProps.voteConfig.displayType === DISPLAY_TYPES.TABLE,
    },
    {
      Component: VotingForm,
      key: "voting-form",
      getProps: (baseProps) => ({
        candidates: baseProps.candidates,
        setSelectedCandidates: baseProps.setSelectedCandidates,
      }),
      condition: (baseProps) =>
        baseProps.voteConfig.displayType === DISPLAY_TYPES.NONE,
    },
  ],
};

Step 2: Create the Factory

Once the configuration is defined, you can create the CandidateDisplayFactory by calling createDynamicComponentFactory with the configuration.

export const CandidateDisplayFactory = createDynamicComponentFactory(
  candidateDisplayConfig
);

Step 3: Generate Dynamic Components

To render the correct candidate display format, you can use the generateItems function of the factory. This will return an array of components based on the current baseProps.

const renderedComponents = CandidateDisplayFactory.generateItems(baseProps);

Where baseProps is the object containing properties such as the candidates, selected candidates, vote status, and display type.


Steps for Extending

The Component Factory is highly extensible. To extend the system with new components or functionalities, follow these steps:

1. Define a New Component

Create a new React component that will be part of the dynamic component rendering. Ensure that the component accepts the appropriate props that will be passed through the factory.

Example:

const NewCustomComponent = ({ candidates, status }) => {
  return (
    <div>
      {/* Render the component */}
    </div>
  );
};

2. Add Component Configuration

Once the component is created, you need to add it to the FactoryConfig configuration object. Define its key, getProps, and condition (if needed).

Example:

const newComponentConfig: ComponentConfig = {
  Component: NewCustomComponent,
  key: "new-custom-component",
  getProps: (baseProps) => ({
    candidates: baseProps.candidates,
    status: baseProps.status,
  }),
  condition: (baseProps) => baseProps.voteConfig.displayType === "NEW_TYPE",
};

3. Update the Factory Configuration

Add the new component configuration to the items array in the FactoryConfig.

Example:

const candidateDisplayConfig: FactoryConfig = {
  items: [
    ...existingItems,
    newComponentConfig, // Add your new component here
  ],
};

4. Use the Updated Factory

To use the new component, you don’t need to change any existing code that uses the factory. Simply call the generateItems method, and the factory will automatically render the new component based on the configuration.

Example:

const renderedComponents = CandidateDisplayFactory.generateItems(baseProps);

5. (Optional) Add Conditional Logic

If your new component should only render under specific conditions, define the condition function within the ComponentConfig to filter when the component should be included in the render output. This allows for greater flexibility in displaying components based on dynamic configurations.

Example:

condition: (baseProps) => baseProps.voteConfig.displayType === "NEW_TYPE",

With these steps, you can easily extend the Component Factory pattern by adding new components, updating configurations, and controlling the rendering logic through conditions. This ensures that the system remains modular, maintainable, and flexible to future changes.

Clone this wiki locally