Búsqueda de sitios web

reactjs - Renderizado dinámico con React y Typescript


Usando React con TypeScript, intento renderizar componentes dinámicamente basándome en el nombre de un campo

// 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} />;
};

Mis tipos:

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

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

type ComponentType = ComponentTypeA | ComponentTypeB;

El compilador no está contento porque no sabe qué tipo usar (Component1 o Component2, que tienen propiedades diferentes). Recibo este error

El tipo 'ComponentType' no se puede asignar al tipo 'ComponentTypeA & ComponentTypeB'.

El tipo 'ComponentTypeA' no se puede asignar al tipo 'ComponentTypeB & ComponentTypeA'.

Al tipo 'ComponentTypeA' le faltan las siguientes propiedades del tipo 'ComponentTypeB': excerpt

Así que he intentado de esta manera:

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} />;
  }
};

Pero el error permanece incluso con la prueba instanceof.

Respuesta #1

Podrías usar un guardia de tipo:

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

Y lo usarías en la condición:

  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
  }

Respuesta #2

Terminé resolviendo este error de esta manera:

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;
  }
};

Pero estoy bastante descontento con esto, porque si tengo que tratar con muchos componentes, la declaración switch será muy larga... Se agradece un mejor enfoque.