Home > front end >  Dynamic rendering with React and Typescript
Dynamic rendering with React and Typescript

Time:05-22

Using React with TypeScript, I try to dynamically render components based on a field name

// Referencing all components
const components = {
  ComponentA,
  ComponentB,
};

// Dynamic render the component based on fieldName
const displayComponent = (
  fieldName: keyof typeof components,
  content: ComponentType
): React.ReactNode => {
  const FieldNameComponent = components[fieldName];
  return <FieldNameComponent item={content} />;
};

My types:

type ComponentTypeA = {
  title: string;
  content: string;
};

type ComponentTypeB = {
  title: string;
  excerpt: string;
};

type ComponentType = ComponentTypeA | ComponentTypeB;

The compiler is not happy because it doesn't know which type to use (Component1 or Component2 which have different properties). I get this error

Type 'ComponentType' is not assignable to type 'ComponentTypeA & ComponentTypeB'.

Type 'ComponentTypeA' is not assignable to type 'ComponentTypeB & ComponentTypeA'.

Type 'ComponentTypeA' is missing the following properties from type 'ComponentTypeB': excerpt

So I have tried this way:

const displayComponent = (
  fieldName: keyof typeof components,
  content: ComponentType
) => {
  if (content instanceof ComponentTypeA) {
    return <ComponentA item={content} />;
  } else if (content instanceof ComponentTypeB) {
    return <ComponentB item={content} />;
  }
};

But the error remains even with the instanceof test.

CodePudding user response:

You could use a type guard:

function isComponentTypeA(type: unknown): type is ComponentTypeA {
    return type && "title" in type && "content" in type;
}

And you would use it in the condition:

  if (isComponentTypeA(content)) {
    return <ComponentA item={content} />; // inferred as ComponentTypeA
  } else if (isComponentTypeB(content)) { // another type guard if needed
    return <ComponentB item={content} />; // inferred as ComponentTypeB
  }

CodePudding user response:

I ended up to solve this error this way:

const displayComponent = (
  content: ComponentType
): React.ReactNode => {
  switch (content.fieldName) {
    case "ComponentA":
      return <ComponentA item={ content as ComponentAType}/>
    case "ComponentB":
      return <ComponentB item={ content as ComponentBType}/>
    default:
      break;
  }
};

But I pretty unhappy with this, because if I have to deal with a lot to components, the switch statement will be so long... A better approach is welcome.

  • Related