Skip to content
Advertisement

How do I pass an array of objects that contains a component to child component in React with Typescript?

I need to make Accordion component reusable. In the future I should be able to add new object to the same array ‘accordionProps’ and pass it down to accordion.

Here I’m passing an array ‘accordionProps’ of objects down to component. Prop types are defined in child component Accordion which matches my array content.

     const accordionProps = [
    {
      title: 'Active Orders',
      body: <OrderModule />,
    },
    {
      title: 'Drivers',
      body: <DriverModule />,
    },
  ];

  return (
    <DashboardWrapper>
      <Accordion title={title} body={body}/>
      <Map />
    </DashboardWrapper>
  );
};

export default Dashboard;

Still TypeScript throws an error:

Type ‘{ accordionProps: { title: string; body: Element; }[]; }’ is not assignable to type ‘IntrinsicAttributes & accordionProps’. Property ‘accordionProps’ does not exist on type ‘IntrinsicAttributes & accordionProps’.

It doesn’t work when I do pass props as {…accordionProps} either.

Here is what my child component Accordion looks like:

import { Component } from 'react';

type accordionProps = [
  {
    title: string;
    body: Component;
  },
];

const Accordion = (props: accordionProps) => {
  return (
    <AccordionContainer>
      {props.map(section => (
        <div key={section.title}>
          <div>{section.title})</div>
          <div>{section.body}</div>
        </div>
      ))}
    </AccordionContainer>
  );
};

export default Accordion;

I can’t figure why TS is not letting me do that.

Does anyone know if an abstraction like that is even possible in React?

Help is really appreciated.

Code Sandbox

Answer

You declared your prop types as an array with one object in it:

type AccordionProps = [
  {
    title: string;
    body: Component;
  },
];

However, react props must be an object:

type AccordionProps = {
  title: string;
  body: Component;
},

Then, in order to render an array of items, you need to map over the array of props, rendering the component you want in each iteration of the loop.

For example, assuming you had:

  // declare an array of AccordionProps objects
  const accordionProps: AccordionProps[] = [
    {
      title: 'Active Orders',
      body: <OrderModule />,
    },
    {
      title: 'Drivers',
      body: <DriverModule />,
    },
  ];

Then you might render those like so:

<>
  { accordionProps.map(props => <Accordion {...props} />) }
</>

Update from your complete example:

React components always take an object for props, never an array. One of those props may be an array, but the props as a whole must be an object. So props.map is never right.

So first, let’s fix your Accordion component:

type AccordionSection = {
  title: string;
  body: ReactNode;
};

type AccordionProps = {
  sections: AccordionSection[];
};

const Accordion = (props: AccordionProps) => {
  return (
    <div>
      {props.sections.map((section) => (
        <div key={section.title}>
          <div>{section.title})</div>
          <div>{section.body}</div>
        </div>
      ))}
    </div>
  );
};

Here props is an object with a single property sections. sections is type to be an array of AccordionSection objects. That means you can iterate through those sections with props.sections.map.

Also note that I change the type of body from Component to ReactNode. Component is a type for renderable components, where ReactNode is the type for already rendered content. function MyComponent() { return <></> } is a component. <MyComponent /> is a ReactNode.

Now when you call this component, you have to pass in the sections prop as an array of AccordionSection objects.

import Accordion, { AccordionSection } from "../Accordion";

export default function App() {
  const accordionSections: AccordionSection[] = [
    {
      title: "Active Orders",
      body: <OrderModule />
    },
    {
      title: "Drivers",
      body: <DriverModule />
    }
  ];

  return (
    <div className="App">
      <Accordion sections={accordionSections} />
    </div>
  );
}

Updated Sandbox

Advertisement