Young L.
Young L.

Reputation: 1042

How can i create custom component dynamically by string

Hi I need to help with my method for creating list of custom components. This method has same logic for 4 components (Package,Theme,Part, Knowledges). Only one thing i have to change is component which i pushing to list.

I tried something like this, but this throw weird error:

index.js:1 Warning: <Package /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.
export class SubjectDashboardUtils {
    /**
     * Make list of Components of certain type
     * @param {[]} list
     * @param {Number} itemType
     * @return {any} items
     */
    static makeListOfTypedItems(list: [], itemType: string): any {
        console.log(list);
        console.log(itemType);
        const items = [];
        list.forEach((item, key) => {
            const subjectPartComponent = React.createElement('Package', {key: key, data: item}, itemType);
            items.push(subjectPartComponent);
        });
        return items;
    }
}

Upvotes: 0

Views: 564

Answers (2)

Jacob
Jacob

Reputation: 78840

React.createElement needs the component class/function to create components, not a string. Strings are only for creating DOM elements, and those must start with a lowercase letter.

Do this instead:

import Package from '...';

React.createElement(Package, ...);

If you need to parameterize the creation of elements, you can just pass component types around like any other variable. For example, if you wanted to change makeListOfTypedItems to use a dynamic component type, it could look something like this (sorry, don't get your original code's intent, so they might not be what you meant):

import { ComponentType } from 'react';

// ...

export class SubjectDashboardUtils {
  static makeListOfTypedItems<
    DataType, 
    Cmp extends ComponentType<{ data: DataType }>
  >(list: Array<DataType>, DynamicComponent: Cmp) {
    return list.map((item, key) => <DynamicComponent key={key} data={item}/>);
  }
}

This would be called like:

SubjectDashboardUtils.makeListOfTypedItems(items, Package);

Upvotes: 0

Akxe
Akxe

Reputation: 11495

You should not use strings are they are not treed shakable. Here is a type-safe solution.

export type TypedItems = Package | Theme | Part | Knowledges;

export class SubjectDashboardUtils {
    static makeListOfTypedItems<T extends TypedItems>(list: any[], itemType: {new(): T; }): (T)[] {
        console.log(list);
        console.log(itemType);
        const items = [];
        return list.map((data, key) => {
            return React.createElement(itemType, {key, data})
        });
    }
}


// Use it like so

const packages = SubjectDashboardUtils.makeListOfTypedItems(['data'], Package);
const parts = SubjectDashboardUtils.makeListOfTypedItems(['data'], Part);

The benefit of this is inheriting type safety. You could probably even have a type of list not being any, but for that, I do not know enough of React.

Upvotes: 1

Related Questions