'React render array of components

Quick question. Anyone know how to render an array of components? Trying to make it easier for a developer to alter a particular component. (It's like a dashboard).

Component list file

import React from 'react';
export default [
    <ComponentOne/>
    <ComponentTwo/>
];

Dashboard Component

import React from 'react';

import components from './../../components';

export default class Dashboard extends React.Component 
{
    render = () => {
        //Want to render the array of components here.
        return (
            <div className="tile is-parent">
                {components}
            </div>
        );
    };
}

The issue is I have an array of components that I need to add a key to. However! I can't seem to add a key to the component as well, not sure how to explain it really so here's the code I've tried:

{components.map((component, key) => (
    <component key={key}/>
}

If I do the above I get no 'you must apply a key' errors however nothing renders? And I'm guessing it's because 'component' doesn't exist or something weird along those lines.

I've also tried component.key = key; but it doesn't let me do that on this type of Object apparently?

My fallback I suppose is to return a shorthand function instead of an array but I like the array for some reason? Seems simpler for juniors.



Solution 1:[1]

Have you consider using the new React Fragments? (in v16)

This would be the simplest solution as it would by pass the whole array/key issue.

If you need to pass key, then I'd suggest to simply require the components to have the keys. This is how React works, so I wouldn't suggest you to hide this behavior behind an interface that might not be predictable.

If you really need to do this, then you can use React.cloneElement to clone the element and inject new properties:

React.cloneElement(element, { key: 'foo' });

Solution 2:[2]

If you’re always going to want to render all the components in your components file then you’re probably better off wrapping them in a React.Fragments tag.

Best practise is just to export this as a simple function that returns the components rather than as a constant.

So...

const Components = props => {
  return (
    <React.Fragment>

      <ComponentOne/>
      <ComponentTwo/>

    </React.Fragment>
  )
}

export default Components

That allows you to put multiple components next to each other without a DOM element containing them.

You should then just be able to render that by using it as a normal component and it’ll render all of them, so just import it then...

<Components />

Otherwise, if you want to treat them like an array, you have a function for free on the React object you’ve imported...

React.Children.toArray(arrayOfComponents)

You pass it an array of components (like in your original question) and it allows you to sort and slice it if you need to then you should be able to just drop it in the return of your render function

Solution 3:[3]

Following up with my comment, you should be doing this instead:

{components.map((component, index) => (
    <span key={index}>
        { component }
    </span>
}

With React 16, you can use React.Fragment:

{components.map((component, index) => (
    <React.Fragment key={index}>
        { component }
    </React.Fragment>
}

Solution 4:[4]

All of these answers are almost right. Just remove the <../> from your exports:

export default [
    ComponentOne,
    ComponentTwo,
]

And in the other file use .map():

export default class Dashboard extends React.Component {
    render = () => (
        <div className="tile is-parent">
            {components.map((Component, key) => (<Component key={key} />))}
        </div>
    )
}

Also note that if you wanted to use Fragment like others suggested you can just write <>...</> instead.

Solution 5:[5]

It's pretty easy, just wrap your component into div and pass key there as i did below:

const Example = ({components}) => (
  <div>
    {components.map((component, i) => <div key={i}>{component}</div>)}    
  </div>
)

Worked example

Solution 6:[6]

Actually you are exporting array of elements from the file. One way is to export array of component and render them like

import React from 'react';
export default [
    ComponentOne
    ComponentTwo
];
// Then following will work
{components.map((Component, key) => (
    // Remember to make first letter capital (in this case "c")
    <Component key={key}/>
}

The other way is to wrap the component in div like this

import React from 'react';
export default [
    <ComponentOne/>
    <ComponentTwo/>
];
// Then wrap in div
{components.map((component, key) => (
    <div key={key}>
      {component}
    </div>
}

Solution 7:[7]

You can also do like that :

{components.map(component => component)}

It mappes your components to display them one by one.

Solution 8:[8]

Just to expand a bit more to the accepted answer. If you have an array of elements elementArray and you would like to pass props to it like callbacks etc. you would do something like this-

To create the component array somewhere-

elementArray.push(<MyComponent/>);

And then in render-

<div>
{
    elementArray.map((element,i) => {
       React.cloneElement(element, { key: i,  onClick: () => myOnclick(i)})
    })
}
</div>

The second argument to React.cloneElement is an object of all the props that you would pass to the component at the time of render.

Solution 9:[9]

if you are using react version above 16 you can create your list of component like that:

import React from 'react';

export const componentList = [ ComponentOne, ComponentTwo ];

and render them anywhere like that:

import React from 'react';

function SomeComponent() {
  return (
    <div>
      {componentList.map((component, index) => (
        <div key={index}> {component && component.render()}</div>
      ))}
    </div>
  );
}

Solution 10:[10]

If I put components inside an array, I usually do it like this:

const COMPONENTS = [
  <Foo />,
  <Bar />
]

[0, 1].map(item => {
      return (
        <React.Fragment key={item}>
          {COMPONENTS[item]}
        </React.Fragment>
      )
})

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Simon Boudrias
Solution 2 zbr
Solution 3
Solution 4 codejockie
Solution 5 The Reason
Solution 6
Solution 7 Forth
Solution 8 AnBisw
Solution 9 Muho
Solution 10 congdc