How to dynamically build an object for a React Component using props?

Viewed 62

I have the component pasted below that outputs a grid of 6 items. These 6 items are currently hardcoded as gridItems.

I would like to understand how I can make this component dynamic. Where I can tell the component, via props, which items (designer, manager, ...) to render.

Currently, the component will output 6 items. How can I make it so by doing something like:

<RoleCardGrid designer manager />

Renders the component with only 2 of the items.

Example 2:

<RoleCardGrid designer manager doctor sales />

Renders the component with only 4 of the items.

Thank you for the help.

Here is my current component:

import React from 'react';
import RoleCard from 'components/RoleCard';

let gridItems = [
  { roleTitle: "designer",
    linkTo: "example.com",
    description: "Desc goes here",
  },
  { roleTitle: "manager",
    linkTo: "example.com",
    description: "Desc goes here",
  },
  { roleTitle:"freelancer",
    linkTo: "example.com",
    description: "Desc goes here",
  },
  { roleTitle:"engineer",
    linkTo: "example.com",
    description: "Desc goes here",
  },
  { roleTitle:"doctor",
    linkTo: "example.com",
    description: "Desc goes here",
  },
  { roleTitle:"sales",
    linkTo: "example.com",
    description: "Desc goes here",
  }
];

function renderGridItems(items) {
  let rendered = items.map((item, index) => {
    return (<div key={`gridItem-${index}`}>
      <RoleCard
        key={`roleGridItem-${index}`}
        roleTitle={item.roleTitle}
        linkTo={item.linkTo}
        description={item.description}
      />
    </div>);
  });
  return rendered;
}

const RoleCardGrid = () => (
  <Grid
    maxColumns={[1, 2, 2]}
    gridGap={[40, 40, 40]}
    maxWidth={[null, 760, 760]}
    rcLabel="rolePages"
  >
    {renderGridItems(gridItems)}
  </Grid>
);


export default RoleCardGrid;
2 Answers

You can either introduce a flag property for each role, or you can expose a specialized RoleCard component for each role. The markup will look different. I will quickly sketch both solutions:

flag properties

// each role can only be shown once
// fixed order of roles

//usage
<RoleCardGrid designer manager doctor sales />

//metadata
const gridItems = {
  designer: { roleTitle: "designer", linkTo: "example.com", reasoning:"Desc goes here" },
  doctor: { roleTitle: "doctor", linkTo: "example.com", reasoning:"Desc goes here" },
  // etc... add all roles here ...
};

//component
const RoleCardGrid = ({designer, manager, doctor, sales, freelancer, engineer}) => {

  return (
    <Grid
      maxColumns={[1, 2, 2]}
      gridGap={[40, 40, 40]}
      maxWidth={[null, 760, 760]}
      rcLabel="rolePages"
    >
      {designer && <RoleCard {...gridItems.designer} />}
      {manager && <RoleCard {...gridItems.manager} />}
      {doctor && <RoleCard {...gridItems.doctor} />}
      {sales && <RoleCard {...gridItems.sales} />}
      {freelancer && <RoleCard {...gridItems.freelancer} />}
      {engineer && <RoleCard {...gridItems.engineer} />}
    </Grid>
  );
};

specialized RoleCard component

// each role can occur more than once
// order of roles can be varied

//usage
<RoleCardGrid>
  <RoleCardGrid.Designer />
  <RoleCardGrid.Manager />
  <RoleCardGrid.Doctor />
  <RoleCardGrid.Sales />
<RoleCardGrid>

//metadata
const gridItems = {
  designer: { roleTitle: "designer", linkTo: "example.com", reasoning:"Desc goes here" },
  doctor: { roleTitle: "doctor", linkTo: "example.com", reasoning:"Desc goes here" },
  // etc... add all roles here ...
};

//component
const RoleCardGrid = ({ children }) => {

  return (
    <Grid
      maxColumns={[1, 2, 2]}
      gridGap={[40, 40, 40]}
      maxWidth={[null, 760, 760]}
      rcLabel="rolePages"
    >
      {children}
    </Grid>
  );
};

RoleCardGrid.Designer = () => (<RoleCard {...gridItems.designer} />);
RoleCardGrid.Manager = () => (<RoleCard {...gridItems.manager} />);
RoleCardGrid.Doctor= () => (<RoleCard {...gridItems.doctor} />);
RoleCardGrid.Sales = () => (<RoleCard {...gridItems.sales} />);
RoleCardGrid.Freelancer = () => (<RoleCard {...gridItems.freelancer} />);
RoleCardGrid.Engineer = () => (<RoleCard {...gridItems.engineer} />);

You could filter gridItems by checking if their roleTitle present in the props.

WARNING: This is just an example for inspiration:

let gridItems = [
   { roleTitle: "designer",
     linkTo: "example.com",
     description: "Desc goes here",
   },
   { roleTitle: "manager",
     linkTo: "example.com",
     description: "Desc goes here",
   },
   { roleTitle:"freelancer",
     linkTo: "example.com",
     description: "Desc goes here",
   },
   { roleTitle:"engineer",
     linkTo: "example.com",
     description: "Desc goes here",
   },
   { roleTitle:"doctor",
     linkTo: "example.com",
     description: "Desc goes here",
   },
   { roleTitle:"sales",
     linkTo: "example.com",
     description: "Desc goes here",
   }
];

// Component
import React, { useMemo } from 'react';

const RoleCardGrid = (props) => {
   const selectedItems = useMemo(() => {
      return gridItems.filter(({ roleTitle }) => roleTitle in props)
   }, [])


   return (
      <div>
         {selectedItems.map((item, index) => (
            <div key={`gridItem-${index}`}>
               <RoleCard
                  key={`roleGridItem-${index}`}
                  roleTitle={item.roleTitle}
                  linkTo={item.linkTo}
                  reasoning={item.reasoning}
               />
            </div>
         ))}
      </div>
   )
}

// Then you can use
<RoleCardGrid designer manager doctor sales />
Related